@@ -61,15 +61,23 @@ impl<'a, T> ParseError<'a, T> {
61
61
}
62
62
63
63
/// The owned input for a parser.
64
- pub struct ParserInput < ' t > {
65
- tokenizer : Tokenizer < ' t > ,
64
+ pub struct ParserInput < ' i > {
65
+ tokenizer : Tokenizer < ' i > ,
66
+ cached_token : Option < CachedToken < ' i > > ,
66
67
}
67
68
68
- impl < ' t > ParserInput < ' t > {
69
+ struct CachedToken < ' i > {
70
+ token : Token < ' i > ,
71
+ start_position : tokenizer:: SourcePosition ,
72
+ end_position : tokenizer:: SourcePosition ,
73
+ }
74
+
75
+ impl < ' i > ParserInput < ' i > {
69
76
/// Create a new input for a parser.
70
- pub fn new ( input : & ' t str ) -> ParserInput < ' t > {
77
+ pub fn new ( input : & ' i str ) -> ParserInput < ' i > {
71
78
ParserInput {
72
79
tokenizer : Tokenizer :: new ( input) ,
80
+ cached_token : None ,
73
81
}
74
82
}
75
83
}
@@ -348,11 +356,49 @@ impl<'i: 't, 't> Parser<'i, 't> {
348
356
if let Some ( block_type) = self . at_start_of . take ( ) {
349
357
consume_until_end_of_block ( block_type, & mut self . input . tokenizer ) ;
350
358
}
359
+
351
360
let byte = self . input . tokenizer . next_byte ( ) ;
352
361
if self . stop_before . contains ( Delimiters :: from_byte ( byte) ) {
353
362
return Err ( BasicParseError :: EndOfInput )
354
363
}
355
- let token = self . input . tokenizer . next ( ) . map_err ( |( ) | BasicParseError :: EndOfInput ) ?;
364
+
365
+ let token_start_position = self . input . tokenizer . position ( ) ;
366
+ let token;
367
+ match self . input . cached_token {
368
+ Some ( ref cached_token) if cached_token. start_position == token_start_position => {
369
+ self . input . tokenizer . reset ( cached_token. end_position ) ;
370
+ token = cached_token. token . clone ( ) ;
371
+ }
372
+ _ => {
373
+ token = self . input . tokenizer . next ( ) . map_err ( |( ) | BasicParseError :: EndOfInput ) ?;
374
+ match token {
375
+ // Don’t cache whitespace or comment tokens.
376
+ // A typical pattern is:
377
+ //
378
+ // ```
379
+ // parser.try(|parser| {
380
+ // match parser.next() { … }
381
+ // }).or_else(|| {
382
+ // match parser.next() { … }
383
+ // })
384
+ // ```
385
+ //
386
+ // If the curren position at the start of this code is at a whitespace token,
387
+ // the "interesting" token (returned by `next`) comes later.
388
+ // So in the second call to `next`, we don’t want "uninteresting" tokens
389
+ // to overwrite the cache.
390
+ Token :: WhiteSpace ( _) | Token :: Comment ( _) => { }
391
+ _ => {
392
+ self . input . cached_token = Some ( CachedToken {
393
+ token : token. clone ( ) ,
394
+ start_position : token_start_position,
395
+ end_position : self . input . tokenizer . position ( ) ,
396
+ } )
397
+ }
398
+ }
399
+ }
400
+ }
401
+
356
402
if let Some ( block_type) = BlockType :: opening ( & token) {
357
403
self . at_start_of = Some ( block_type) ;
358
404
}
0 commit comments