@@ -127,6 +127,14 @@ pub enum Count<'a> {
127
127
CountImplied ,
128
128
}
129
129
130
+ pub struct ParseError {
131
+ pub description : string:: String ,
132
+ pub note : Option < string:: String > ,
133
+ pub label : string:: String ,
134
+ pub start : usize ,
135
+ pub end : usize ,
136
+ }
137
+
130
138
/// The parser structure for interpreting the input format string. This is
131
139
/// modeled as an iterator over `Piece` structures to form a stream of tokens
132
140
/// being output.
@@ -137,7 +145,7 @@ pub struct Parser<'a> {
137
145
input : & ' a str ,
138
146
cur : iter:: Peekable < str:: CharIndices < ' a > > ,
139
147
/// Error messages accumulated during parsing
140
- pub errors : Vec < ( string :: String , Option < string :: String > ) > ,
148
+ pub errors : Vec < ParseError > ,
141
149
/// Current position of implicit positional argument pointer
142
150
curarg : usize ,
143
151
}
@@ -160,12 +168,17 @@ impl<'a> Iterator for Parser<'a> {
160
168
}
161
169
'}' => {
162
170
self . cur . next ( ) ;
171
+ let pos = pos + 1 ;
163
172
if self . consume ( '}' ) {
164
- Some ( String ( self . string ( pos + 1 ) ) )
173
+ Some ( String ( self . string ( pos) ) )
165
174
} else {
166
- self . err_with_note ( "unmatched `}` found" ,
167
- "if you intended to print `}`, \
168
- you can escape it using `}}`") ;
175
+ self . err_with_note (
176
+ "unmatched `}` found" ,
177
+ "unmatched `}`" ,
178
+ "if you intended to print `}`, you can escape it using `}}`" ,
179
+ pos,
180
+ pos,
181
+ ) ;
169
182
None
170
183
}
171
184
}
@@ -191,15 +204,40 @@ impl<'a> Parser<'a> {
191
204
/// Notifies of an error. The message doesn't actually need to be of type
192
205
/// String, but I think it does when this eventually uses conditions so it
193
206
/// might as well start using it now.
194
- fn err ( & mut self , msg : & str ) {
195
- self . errors . push ( ( msg. to_owned ( ) , None ) ) ;
207
+ fn err < S1 : Into < string:: String > , S2 : Into < string:: String > > (
208
+ & mut self ,
209
+ description : S1 ,
210
+ label : S2 ,
211
+ start : usize ,
212
+ end : usize ,
213
+ ) {
214
+ self . errors . push ( ParseError {
215
+ description : description. into ( ) ,
216
+ note : None ,
217
+ label : label. into ( ) ,
218
+ start,
219
+ end,
220
+ } ) ;
196
221
}
197
222
198
223
/// Notifies of an error. The message doesn't actually need to be of type
199
224
/// String, but I think it does when this eventually uses conditions so it
200
225
/// might as well start using it now.
201
- fn err_with_note ( & mut self , msg : & str , note : & str ) {
202
- self . errors . push ( ( msg. to_owned ( ) , Some ( note. to_owned ( ) ) ) ) ;
226
+ fn err_with_note < S1 : Into < string:: String > , S2 : Into < string:: String > , S3 : Into < string:: String > > (
227
+ & mut self ,
228
+ description : S1 ,
229
+ label : S2 ,
230
+ note : S3 ,
231
+ start : usize ,
232
+ end : usize ,
233
+ ) {
234
+ self . errors . push ( ParseError {
235
+ description : description. into ( ) ,
236
+ note : Some ( note. into ( ) ) ,
237
+ label : label. into ( ) ,
238
+ start,
239
+ end,
240
+ } ) ;
203
241
}
204
242
205
243
/// Optionally consumes the specified character. If the character is not at
@@ -222,19 +260,26 @@ impl<'a> Parser<'a> {
222
260
/// found, an error is emitted.
223
261
fn must_consume ( & mut self , c : char ) {
224
262
self . ws ( ) ;
225
- if let Some ( & ( _ , maybe) ) = self . cur . peek ( ) {
263
+ if let Some ( & ( pos , maybe) ) = self . cur . peek ( ) {
226
264
if c == maybe {
227
265
self . cur . next ( ) ;
228
266
} else {
229
- self . err ( & format ! ( "expected `{:?}`, found `{:?}`" , c, maybe) ) ;
267
+ self . err ( format ! ( "expected `{:?}`, found `{:?}`" , c, maybe) ,
268
+ format ! ( "expected `{}`" , c) ,
269
+ pos + 1 ,
270
+ pos + 1 ) ;
230
271
}
231
272
} else {
232
- let msg = & format ! ( "expected `{:?}` but string was terminated" , c) ;
273
+ let msg = format ! ( "expected `{:?}` but string was terminated" , c) ;
274
+ let pos = self . input . len ( ) + 1 ; // point at closing `"`
233
275
if c == '}' {
234
276
self . err_with_note ( msg,
235
- "if you intended to print `{`, you can escape it using `{{`" ) ;
277
+ format ! ( "expected `{:?}`" , c) ,
278
+ "if you intended to print `{`, you can escape it using `{{`" ,
279
+ pos,
280
+ pos) ;
236
281
} else {
237
- self . err ( msg) ;
282
+ self . err ( msg, format ! ( "expected `{:?}`" , c ) , pos , pos ) ;
238
283
}
239
284
}
240
285
}
@@ -300,6 +345,15 @@ impl<'a> Parser<'a> {
300
345
} else {
301
346
match self . cur . peek ( ) {
302
347
Some ( & ( _, c) ) if c. is_alphabetic ( ) => Some ( ArgumentNamed ( self . word ( ) ) ) ,
348
+ Some ( & ( pos, c) ) if c == '_' => {
349
+ let invalid_name = self . string ( pos) ;
350
+ self . err_with_note ( format ! ( "invalid argument name `{}`" , invalid_name) ,
351
+ "invalid argument name" ,
352
+ "argument names cannot start with an underscore" ,
353
+ pos + 1 , // add 1 to account for leading `{`
354
+ pos + 1 + invalid_name. len ( ) ) ;
355
+ Some ( ArgumentNamed ( invalid_name) )
356
+ } ,
303
357
304
358
// This is an `ArgumentNext`.
305
359
// Record the fact and do the resolution after parsing the
0 commit comments