-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.go
More file actions
208 lines (177 loc) · 5.54 KB
/
Copy pathparser.go
File metadata and controls
208 lines (177 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Package parser implements a parser that converts [lexer.Token] into an AST.
package parser
import (
"slices"
"github.com/ProCode-Software/klar/internal/klarerrs"
"github.com/ProCode-Software/klar/internal/lexer"
)
const (
isAttribute = 1 << iota // Allow version parsing
whenPattern // Allow '_'
)
// A Parser parses lexer tokens into an abstract syntax tree (AST).
type Parser struct {
Options Options
Tokens []lexer.Token
Index int
Errors []*Error
flags uint8 // Conditional flags
listCastTokens map[int]struct{} // Populated during EOS inference
}
// Error is [klarerrs.Error]
type Error = klarerrs.Error
// Options is options provided to [Parser].
type Options struct {
// Path of the file being parsed. File is applied to all reported errors.
File string
// If Error != nil, Error is called for every reported error.
Error func(e *Error)
// Parsing is stopped once len(p.Errors) equals MaxErrors.
// If MaxErrors <= 0, there is no limit.
MaxErrors int
}
// New returns a new [Parser] that reads from tokens. If options == nil,
// default options are used.
func New(tokens []lexer.Token, options *Options) *Parser {
if options == nil {
options = &Options{}
}
return &Parser{
Tokens: tokens,
Index: 0,
Options: *options,
}
}
// Curr return the [lexer.Token] at the current parser index.
func (p *Parser) Curr() lexer.Token { return p.Tokens[p.Index] }
// PeekBehind return the [lexer.Token] before the current parser index.
func (p *Parser) PeekBehind() lexer.Token { return p.Tokens[p.Index-1] }
// Peek return the next [lexer.Token] without advancing the parser.
func (p *Parser) Peek() lexer.Token {
if !p.HasTokens() {
return p.Curr()
}
return p.Tokens[p.Index+1]
}
func (p *Parser) PeekKind() lexer.TokenType { return p.Peek().Kind }
// CurrKind return the Kind of the [lexer.Token] at the current parser index.
func (p *Parser) CurrKind() lexer.TokenType { return p.Curr().Kind }
// Backup decrements the parser's index by 1.
func (p *Parser) Backup() {
if p.Index <= 0 {
panic("Parser.Backup() called with Index 0")
}
p.Index--
}
// Advance returns the current Token and increases the parser index.
func (p *Parser) Advance() lexer.Token {
tok := p.Curr()
if tok.Kind == lexer.EOF {
return tok
}
p.Index++
return tok
}
// HasTokens reports whether the parser is not at EOF.
func (p *Parser) HasTokens() bool {
return p.Index < len(p.Tokens) && p.CurrKind() != lexer.EOF
}
// WhileNot reports whether the current token kind is not kind and the parser is not at EOF.
func (p *Parser) WhileNot(kind lexer.TokenType) bool {
return p.HasTokens() && p.CurrKind() != kind
}
// WhileNotEndOr reports whether the current token kind is not kind and the parser is
// not at EOF or EOS.
func (p *Parser) WhileNotEndOr(kind lexer.TokenType) bool {
return p.HasTokens() &&
p.CurrKind() != lexer.Newline &&
p.CurrKind() != kind
}
// IsCurrently reports whether the current token is one of kinds.
func (p *Parser) IsCurrently(kinds ...lexer.TokenType) bool {
return slices.Contains(kinds, p.CurrKind())
}
// IsNotCurrentlyEndOr returns true if the current token is not EOF, EOS. or kind.
func (p *Parser) IsNotCurrentlyEndOr(kind lexer.TokenType) bool {
curr := p.CurrKind()
return p.HasTokens() && curr != lexer.Newline && curr != kind
}
// Expect advances the parser if the current token is of typ, otherwise throws an
// ExpectedTokenError.
func (p *Parser) Expect(exp lexer.TokenType, expFlags ...expectFlag) lexer.Token {
got := p.Curr()
if got.Kind == exp {
return p.Advance()
}
err, noAdvance := withExpectFlags(expFlags, exp, got)
p.Error(err)
if noAdvance {
return got
}
return p.Advance()
}
func (p *Parser) ExpectOneOf(a, b lexer.TokenType, expFlags ...expectFlag) lexer.Token {
got := p.Curr()
if got.Kind == a || got.Kind == b {
return p.Advance()
}
err, noAdvance := withExpectFlags(expFlags, a, got)
p.Error(err)
if noAdvance {
return got
}
return p.Advance()
}
// If stopParsing is passed to panic, the parser will immediately stop parsing.
type stopParsing struct{}
type stmtError struct{}
// Error adds an error to the parser.
func (p *Parser) Error(err *klarerrs.Error) {
err.File = p.Options.File
p.Errors = append(p.Errors, err)
if p.Options.Error != nil {
p.Options.Error(err)
}
if mx := p.Options.MaxErrors; mx > 0 && len(p.Errors) >= mx &&
klarerrs.ErrorCount(p.Errors) >= mx { // Exclude warnings from error limit
panic(stopParsing{})
}
}
func (p *Parser) ErrorLabelled(err *klarerrs.Error, label string) {
err.Label = label
p.Error(err)
}
// AdvanceNonBoundary returns the current token advances the parser and returns
// the current token if it is not a boundary, otherwise returns the current token.
// Useful when an expected token is missing.
func (p *Parser) AdvanceNonBoundary() lexer.Token {
c := p.Curr()
switch c.Kind {
case lexer.Newline, lexer.EOF, lexer.RightCurlyBrace,
lexer.RightParenthesis, lexer.RightBracket, lexer.Comma:
default:
p.Advance()
}
return c
}
func (p *Parser) handlePanic() {
switch err := recover(); err.(type) {
case nil, stopParsing: // Bailout
return
default:
panic(err) // Re-panic if other error
}
}
func (p *Parser) isWhenPattern() bool { return (p.flags & whenPattern) != 0 }
func (p *Parser) isAttribute() bool { return (p.flags & isAttribute) != 0 }
// Reset resets all properties to defaults, freeing resources.
func (p *Parser) Reset() {
p.Options.Error = nil
p.Options.File = ""
p.Options.MaxErrors = 0
p.Tokens = nil
p.Index = 0
p.Errors = nil
p.flags = 0
p.listCastTokens = nil
}