Skip to content

Commit f179efb

Browse files
authored
Implement backwards iteration for token.Cursor (#429)
This adds methods to `token.Cursor` to implement backwards iteration with `Prev` and `PrevSkippable`. Methods `Pop` and `PopSkippable` have been renamed to `Next` and `NextSkippable` to match the backward counterparts. The constructor has been altered to `NewCursorAt` which takes a single starting cursor. The bounds of the cursor iteration are now delimited by the open and closing token when iterating. These methods will be useful for reading attached and trailing comments.
1 parent 8199283 commit f179efb

File tree

12 files changed

+270
-93
lines changed

12 files changed

+270
-93
lines changed

experimental/ast/path.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ func (p Path) Components(yield func(PathComponent) bool) {
107107

108108
var sep token.Token
109109
var broken bool
110-
token.NewCursor(first, p.raw.End.In(p.Context())).Rest()(func(tok token.Token) bool {
110+
token.NewCursorAt(first).Rest()(func(tok token.Token) bool {
111+
if tok.ID() > p.raw.End {
112+
// We've reached the end of the path.
113+
return false
114+
}
111115
if tok.Text() == "." || tok.Text() == "/" {
112116
if !sep.IsZero() {
113117
// Uh-oh, empty path component!

experimental/parser/parse_decl.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func parseDecl(p *parser, c *token.Cursor, in taxa.Noun) ast.DeclAny {
4343

4444
var unexpected []token.Token
4545
for !c.Done() && !canStartDecl(first) {
46-
unexpected = append(unexpected, c.Pop())
46+
unexpected = append(unexpected, c.Next())
4747
first = c.Peek()
4848
}
4949
switch len(unexpected) {
@@ -64,15 +64,15 @@ func parseDecl(p *parser, c *token.Cursor, in taxa.Noun) ast.DeclAny {
6464
}
6565

6666
if first.Text() == ";" {
67-
c.Pop()
67+
c.Next()
6868

6969
// This is an empty decl.
7070
return p.NewDeclEmpty(first).AsAny()
7171
}
7272

7373
// This is a bare declaration body.
7474
if canStartBody(first) {
75-
return parseBody(p, c.Pop(), in).AsAny()
75+
return parseBody(p, c.Next(), in).AsAny()
7676
}
7777

7878
// We need to parse a path here. At this point, we need to generate a
@@ -273,7 +273,7 @@ func parseBody(p *parser, braces token.Token, in taxa.Noun) ast.DeclBody {
273273
// parseRange parses a reserved/extensions range.
274274
func parseRange(p *parser, c *token.Cursor) ast.DeclRange {
275275
// Consume the keyword token.
276-
kw := c.Pop()
276+
kw := c.Next()
277277

278278
in := taxa.Extensions
279279
if kw.Text() == "reserved" {
@@ -391,7 +391,7 @@ func tryParseOptions(p *parser, c *token.Cursor, in taxa.Noun) ast.CompactOption
391391
if !canStartOptions(c.Peek()) {
392392
return ast.CompactOptions{}
393393
}
394-
return parseOptions(p, c.Pop(), in)
394+
return parseOptions(p, c.Next(), in)
395395
}
396396

397397
// parseOptions parses a ([]-delimited) compact options list.
@@ -421,7 +421,7 @@ func parseOptions(p *parser, brackets token.Token, _ taxa.Noun) ast.CompactOptio
421421
)
422422
fallthrough
423423
case "=":
424-
c.Pop()
424+
c.Next()
425425
default:
426426
p.Error(errUnexpected{
427427
what: eq,

experimental/parser/parse_def.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ func (defInputs) what(*defParser) taxa.Noun { return taxa.MethodIns }
184184
func (defInputs) canStart(p *defParser) bool { return p.c.Peek().Text() == "(" }
185185

186186
func (defInputs) parse(p *defParser) report.Span {
187-
next := p.c.Pop()
187+
next := p.c.Next()
188188
if next.IsLeaf() {
189189
return report.Span{} // Diagnosed by the lexer.
190190
}
@@ -205,7 +205,7 @@ func (defOutputs) canStart(p *defParser) bool { return p.c.Peek().Text() == "ret
205205
func (defOutputs) parse(p *defParser) report.Span {
206206
// Note that the inputs and outputs of a method are parsed
207207
// separately, so foo(bar) and foo returns (bar) are both possible.
208-
returns := p.c.Pop()
208+
returns := p.c.Next()
209209

210210
var ty ast.TypeAny
211211
list, err := p.Punct(p.c, "(", taxa.KeywordReturns.After())
@@ -314,7 +314,7 @@ func (defOptions) what(*defParser) taxa.Noun { return taxa.CompactOptions }
314314
func (defOptions) canStart(p *defParser) bool { return canStartOptions(p.c.Peek()) }
315315

316316
func (defOptions) parse(p *defParser) report.Span {
317-
next := p.c.Pop()
317+
next := p.c.Next()
318318
if next.IsLeaf() {
319319
return report.Span{} // Diagnosed by the lexer.
320320
}
@@ -333,7 +333,7 @@ func (defBody) what(*defParser) taxa.Noun { return taxa.Body }
333333
func (defBody) canStart(p *defParser) bool { return canStartBody(p.c.Peek()) }
334334

335335
func (defBody) parse(p *defParser) report.Span {
336-
next := p.c.Pop()
336+
next := p.c.Next()
337337
if next.IsLeaf() {
338338
return report.Span{} // Diagnosed by the lexer.
339339
}

experimental/parser/parse_delimited.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (d delimited[T]) iter(yield func(value T, delim token.Token) bool) {
7474
var latest int // The index of the most recently seen delimiter.
7575

7676
if next := d.c.Peek(); slices.Contains(d.delims, next.Text()) {
77-
_ = d.c.Pop()
77+
_ = d.c.Next()
7878
latest = slices.Index(d.delims, next.Text())
7979

8080
d.p.Error(errUnexpected{
@@ -98,13 +98,13 @@ func (d delimited[T]) iter(yield func(value T, delim token.Token) bool) {
9898
break
9999
}
100100

101-
first := d.c.Pop()
101+
first := d.c.Next()
102102
var last token.Token
103103
for !d.c.Done() && !d.start(d.c.Peek()) {
104104
if d.stop != nil && d.stop(d.c.Peek()) {
105105
break
106106
}
107-
last = d.c.Pop()
107+
last = d.c.Next()
108108
}
109109

110110
want := d.what.AsSet()
@@ -151,7 +151,7 @@ func (d delimited[T]) iter(yield func(value T, delim token.Token) bool) {
151151
}
152152
latest = which
153153

154-
next := d.c.Pop()
154+
next := d.c.Next()
155155
if delim.IsZero() {
156156
delim = next
157157
continue

experimental/parser/parse_expr.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func parseExprInfix(p *parser, c *token.Cursor, where taxa.Place, lhs ast.ExprAn
5555
case ":":
5656
return p.NewExprField(ast.ExprFieldArgs{
5757
Key: lhs,
58-
Colon: c.Pop(),
58+
Colon: c.Next(),
5959
Value: parseExprInfix(p, c, where, ast.ExprAny{}, prec+1),
6060
}).AsAny()
6161

@@ -105,7 +105,7 @@ func parseExprInfix(p *parser, c *token.Cursor, where taxa.Place, lhs ast.ExprAn
105105
case "to":
106106
return p.NewExprRange(ast.ExprRangeArgs{
107107
Start: lhs,
108-
To: c.Pop(),
108+
To: c.Next(),
109109
End: parseExprInfix(p, c, taxa.KeywordTo.After(), ast.ExprAny{}, prec),
110110
}).AsAny()
111111
}
@@ -128,7 +128,7 @@ func parseExprPrefix(p *parser, c *token.Cursor, where taxa.Place) ast.ExprAny {
128128
return ast.ExprAny{}
129129

130130
case next.Text() == "-":
131-
c.Pop()
131+
c.Next()
132132
inner := parseExprPrefix(p, c, taxa.Minus.After())
133133
return p.NewExprPrefixed(ast.ExprPrefixedArgs{
134134
Prefix: next,
@@ -151,13 +151,13 @@ func parseExprSolo(p *parser, c *token.Cursor, where taxa.Place) ast.ExprAny {
151151
return ast.ExprAny{}
152152

153153
case next.Kind() == token.String, next.Kind() == token.Number:
154-
return ast.ExprLiteral{Token: c.Pop()}.AsAny()
154+
return ast.ExprLiteral{Token: c.Next()}.AsAny()
155155

156156
case canStartPath(next):
157157
return ast.ExprPath{Path: parsePath(p, c)}.AsAny()
158158

159159
case (next.Text() == "{" || next.Text() == "<" || next.Text() == "[") && !next.IsLeaf():
160-
body := c.Pop()
160+
body := c.Next()
161161
in := taxa.Dict
162162
if next.Text() == "[" {
163163
in = taxa.Array
@@ -225,7 +225,7 @@ func parseExprSolo(p *parser, c *token.Cursor, where taxa.Place) ast.ExprAny {
225225
func peekTokenExpr(p *parser, c *token.Cursor) token.Token {
226226
next := c.Peek()
227227
if next.IsZero() {
228-
token, span := c.JustAfter()
228+
token, span := c.SeekToEnd()
229229
err := errUnexpected{
230230
what: span,
231231
where: taxa.Expr.In(),

experimental/parser/parse_path.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func parsePath(p *parser, c *token.Cursor) ast.Path {
3939

4040
var prevSeparator token.Token
4141
if start.Text() == "." || start.Text() == "/" {
42-
prevSeparator = c.Pop()
42+
prevSeparator = c.Next()
4343
}
4444

4545
var done bool
@@ -60,7 +60,7 @@ func parsePath(p *parser, c *token.Cursor) ast.Path {
6060
want: taxa.NewSet(taxa.Ident, taxa.Parens),
6161
})
6262
}
63-
prevSeparator = c.Pop()
63+
prevSeparator = c.Next()
6464

6565
case next.Kind() == token.Ident:
6666
if !first && prevSeparator.IsZero() {
@@ -72,7 +72,7 @@ func parsePath(p *parser, c *token.Cursor) ast.Path {
7272

7373
end = next
7474
prevSeparator = token.Zero
75-
c.Pop()
75+
c.Next()
7676

7777
case next.Text() == "(":
7878
if !first && prevSeparator.IsZero() {
@@ -98,7 +98,7 @@ func parsePath(p *parser, c *token.Cursor) ast.Path {
9898

9999
end = next
100100
prevSeparator = token.Zero
101-
c.Pop()
101+
c.Next()
102102

103103
default:
104104
if prevSeparator.IsZero() {

experimental/parser/parse_state.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ type parser struct {
3535
func (p *parser) Punct(c *token.Cursor, want string, where taxa.Place) (token.Token, report.Diagnose) {
3636
next := c.Peek()
3737
if next.Text() == want {
38-
return c.Pop(), nil
38+
return c.Next(), nil
3939
}
4040

4141
wanted := taxa.NewSet(taxa.Punct(want, false))
4242
if next.IsZero() {
43-
tok, span := c.JustAfter()
43+
tok, span := c.SeekToEnd()
4444
err := errUnexpected{
4545
what: span,
4646
where: where,

experimental/parser/parse_type.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ func parseTypeImpl(p *parser, c *token.Cursor, where taxa.Place, pathAfter bool)
167167
// Next, look for some angle brackets. We need to do this before draining
168168
// mods, because angle brackets bind more tightly than modifiers.
169169
if angles := c.Peek(); angles.Text() == "<" && !angles.IsLeaf() {
170-
c.Pop() // Consume the angle brackets.
170+
c.Next() // Consume the angle brackets.
171171
generic := p.NewTypeGeneric(ast.TypeGenericArgs{
172172
Path: tyPath,
173173
AngleBrackets: angles,

0 commit comments

Comments
 (0)