@@ -127,6 +127,14 @@ pub enum Count<'a> {
127127 CountImplied ,
128128}
129129
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+
130138/// The parser structure for interpreting the input format string. This is
131139/// modeled as an iterator over `Piece` structures to form a stream of tokens
132140/// being output.
@@ -137,7 +145,7 @@ pub struct Parser<'a> {
137145 input : & ' a str ,
138146 cur : iter:: Peekable < str:: CharIndices < ' a > > ,
139147 /// Error messages accumulated during parsing
140- pub errors : Vec < ( string :: String , Option < string :: String > ) > ,
148+ pub errors : Vec < ParseError > ,
141149 /// Current position of implicit positional argument pointer
142150 curarg : usize ,
143151}
@@ -160,12 +168,17 @@ impl<'a> Iterator for Parser<'a> {
160168 }
161169 '}' => {
162170 self . cur . next ( ) ;
171+ let pos = pos + 1 ;
163172 if self . consume ( '}' ) {
164- Some ( String ( self . string ( pos + 1 ) ) )
173+ Some ( String ( self . string ( pos) ) )
165174 } 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+ ) ;
169182 None
170183 }
171184 }
@@ -191,15 +204,40 @@ impl<'a> Parser<'a> {
191204 /// Notifies of an error. The message doesn't actually need to be of type
192205 /// String, but I think it does when this eventually uses conditions so it
193206 /// 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+ } ) ;
196221 }
197222
198223 /// Notifies of an error. The message doesn't actually need to be of type
199224 /// String, but I think it does when this eventually uses conditions so it
200225 /// 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+ } ) ;
203241 }
204242
205243 /// Optionally consumes the specified character. If the character is not at
@@ -222,19 +260,26 @@ impl<'a> Parser<'a> {
222260 /// found, an error is emitted.
223261 fn must_consume ( & mut self , c : char ) {
224262 self . ws ( ) ;
225- if let Some ( & ( _ , maybe) ) = self . cur . peek ( ) {
263+ if let Some ( & ( pos , maybe) ) = self . cur . peek ( ) {
226264 if c == maybe {
227265 self . cur . next ( ) ;
228266 } 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 ) ;
230271 }
231272 } 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 `"`
233275 if c == '}' {
234276 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) ;
236281 } else {
237- self . err ( msg) ;
282+ self . err ( msg, format ! ( "expected `{:?}`" , c ) , pos , pos ) ;
238283 }
239284 }
240285 }
@@ -300,6 +345,15 @@ impl<'a> Parser<'a> {
300345 } else {
301346 match self . cur . peek ( ) {
302347 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+ } ,
303357
304358 // This is an `ArgumentNext`.
305359 // Record the fact and do the resolution after parsing the
0 commit comments