Skip to content

Commit 4643478

Browse files
nwtnniMarwes
authored andcommitted
Split apart UnrecognizedEOF error variant from UnrecognizedToken (lalrpop#446)
* Split apart UnrecognizedEOF error variant from UnrecognizedToken * Factor out fmt_expected function * Initial attempt at fixing codegen - Match on lookahead when returning error - Use last fixed symbol as location of UnrecognizedEOF - Use `Default::default()` when there are no fixed symbols (?) * Initial implementation of finding latest optional location * Fix UnrecognizedToken errors in `Some` test cases * Fix UnrecognizedEOF test cases * Fix parentheses around pattern error for 1.26 compatibility * Add basic test for UnrecognizedEOF (and hopefully trigger Travis build) * Fix emitted indentation and use travis_wait when testing
1 parent 417f199 commit 4643478

File tree

6 files changed

+130
-66
lines changed

6 files changed

+130
-66
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ install:
1010
- bash tools/ci-install.sh
1111
script:
1212
- RUST_BACKTRACE=1 CARGO_INCREMENTAL=0 cargo build -p lalrpop
13-
- RUST_BACKTRACE=1 CARGO_INCREMENTAL=0 cargo test --all --all-features
13+
- RUST_BACKTRACE=1 CARGO_INCREMENTAL=0 travis_wait cargo test --all --all-features
1414
- bash tools/build-doc
1515
deploy:
1616
provider: pages

lalrpop-test/src/lib.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,18 @@ fn parse_error_map_err() {
240240
}
241241
}
242242

243+
#[test]
244+
fn parse_error_eof_location() {
245+
match expr_intern_tok::ExprParser::new().parse(1, "1 - ") {
246+
Err(ParseError::UnrecognizedEOF { location, .. }) => {
247+
assert_eq!(location, 3);
248+
}
249+
_ => {
250+
panic!("Expected an UnrecognizedEOF error");
251+
}
252+
}
253+
}
254+
243255
#[test]
244256
fn display_parse_error() {
245257
let expr = "(1+\n(2++3))";
@@ -483,8 +495,8 @@ fn error_recovery_eof() {
483495
assert_eq!(
484496
errors.borrow()[0],
485497
ErrorRecovery {
486-
error: ParseError::UnrecognizedToken {
487-
token: None,
498+
error: ParseError::UnrecognizedEOF {
499+
location: (),
488500
expected: vec!["\"-\"".to_string()],
489501
},
490502
dropped_tokens: vec![],
@@ -499,8 +511,8 @@ fn error_recovery_eof_without_recovery() {
499511
let result = error_recovery::ItemParser::new().parse(&errors, tokens);
500512
assert_eq!(
501513
result,
502-
Err(ParseError::UnrecognizedToken {
503-
token: None,
514+
Err(ParseError::UnrecognizedEOF {
515+
location: (),
504516
expected: vec!["\"-\"".to_string()],
505517
})
506518
);
@@ -520,7 +532,7 @@ fn error_recovery_extra_token() {
520532
errors.borrow()[0],
521533
ErrorRecovery {
522534
error: ParseError::UnrecognizedToken {
523-
token: Some(((), Tok::Plus, ())),
535+
token: ((), Tok::Plus, ()),
524536
expected: vec!["\")\"".to_string()],
525537
},
526538
dropped_tokens: vec![((), Tok::Plus, ())],
@@ -542,7 +554,7 @@ fn error_recovery_dont_drop_unrecognized_token() {
542554
errors.borrow()[0],
543555
ErrorRecovery {
544556
error: ParseError::UnrecognizedToken {
545-
token: Some(((), Tok::RParen, ())),
557+
token: ((), Tok::RParen, ()),
546558
expected: vec!["\"-\"".to_string()],
547559
},
548560
dropped_tokens: vec![],
@@ -564,7 +576,7 @@ fn error_recovery_multiple_extra_tokens() {
564576
errors.borrow()[0],
565577
ErrorRecovery {
566578
error: ParseError::UnrecognizedToken {
567-
token: Some(((), Tok::Plus, ())),
579+
token: ((), Tok::Plus, ()),
568580
expected: vec!["\")\"".to_string()],
569581
},
570582
dropped_tokens: vec![((), Tok::Plus, ()), ((), Tok::Plus, ())],
@@ -615,7 +627,7 @@ fn error_recovery_issue_240() {
615627
errors,
616628
vec![ErrorRecovery {
617629
error: ParseError::UnrecognizedToken {
618-
token: Some((6, Tok::Div, 7)),
630+
token: (6, Tok::Div, 7),
619631
expected: vec!["\")\"".to_string()],
620632
},
621633
dropped_tokens: vec![(6, Tok::Div, 7)],

lalrpop-util/src/lib.rs

+49-40
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,29 @@ pub enum ParseError<L, T, E> {
99
/// expect.
1010
InvalidToken { location: L },
1111

12-
/// Generated by the parser when it encounters a token (or EOF) it did not
13-
/// expect.
12+
/// Generated by the parser when it encounters an EOF it did not expect.
13+
UnrecognizedEOF {
14+
/// The end of the final token
15+
location: L,
16+
17+
/// The set of expected tokens: these names are taken from the
18+
/// grammar and hence may not necessarily be suitable for
19+
/// presenting to the user.
20+
expected: Vec<String>,
21+
},
22+
23+
/// Generated by the parser when it encounters a token it did not expect.
1424
UnrecognizedToken {
15-
/// If this is `Some`, then an unexpected token of type `T`
16-
/// was observed, with a span given by the two `L` values. If
17-
/// this is `None`, then EOF was observed when it was not
18-
/// expected.
19-
token: Option<(L, T, L)>,
25+
/// The unexpected token of type `T` with a span given by the two `L` values.
26+
token: (L, T, L),
2027

2128
/// The set of expected tokens: these names are taken from the
2229
/// grammar and hence may not necessarily be suitable for
2330
/// presenting to the user.
2431
expected: Vec<String>,
2532
},
2633

27-
/// Generated by the parser when it encounters additional,
28-
/// unexpected tokens.
34+
/// Generated by the parser when it encounters additional, unexpected tokens.
2935
ExtraToken { token: (L, T, L) },
3036

3137
/// Custom error type.
@@ -49,8 +55,12 @@ impl<L, T, E> ParseError<L, T, E> {
4955
ParseError::InvalidToken { location } => ParseError::InvalidToken {
5056
location: loc_op(location),
5157
},
58+
ParseError::UnrecognizedEOF { location, expected } => ParseError::UnrecognizedEOF {
59+
location: loc_op(location),
60+
expected: expected,
61+
},
5262
ParseError::UnrecognizedToken { token, expected } => ParseError::UnrecognizedToken {
53-
token: token.map(maptok),
63+
token: maptok(token),
5464
expected: expected,
5565
},
5666
ParseError::ExtraToken { token } => ParseError::ExtraToken {
@@ -84,6 +94,23 @@ impl<L, T, E> ParseError<L, T, E> {
8494
}
8595
}
8696

97+
/// Format a list of expected tokens.
98+
fn fmt_expected(f: &mut fmt::Formatter, expected: &[String]) -> fmt::Result {
99+
if !expected.is_empty() {
100+
try!(writeln!(f, ""));
101+
for (i, e) in expected.iter().enumerate() {
102+
let sep = match i {
103+
0 => "Expected one of",
104+
_ if i < expected.len() - 1 => ",",
105+
// Last expected message to be written
106+
_ => " or",
107+
};
108+
try!(write!(f, "{} {}", sep, e));
109+
}
110+
}
111+
Ok(())
112+
}
113+
87114
impl<L, T, E> fmt::Display for ParseError<L, T, E>
88115
where
89116
L: fmt::Display,
@@ -93,37 +120,19 @@ where
93120
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94121
use self::ParseError::*;
95122
match *self {
123+
User { ref error } => write!(f, "{}", error),
96124
InvalidToken { ref location } => write!(f, "Invalid token at {}", location),
97-
UnrecognizedToken {
98-
ref token,
99-
ref expected,
100-
} => {
101-
match *token {
102-
Some((ref start, ref token, ref end)) => try!(write!(
103-
f,
104-
"Unrecognized token `{}` found at {}:{}",
105-
token, start, end
106-
)),
107-
None => try!(write!(f, "Unrecognized EOF")),
108-
}
109-
if !expected.is_empty() {
110-
try!(writeln!(f, ""));
111-
for (i, e) in expected.iter().enumerate() {
112-
let sep = match i {
113-
0 => "Expected one of",
114-
_ if i < expected.len() - 1 => ",",
115-
// Last expected message to be written
116-
_ => " or",
117-
};
118-
try!(write!(f, "{} {}", sep, e));
119-
}
120-
}
121-
Ok(())
125+
UnrecognizedEOF { ref location, ref expected } => {
126+
try!(write!(f, "Unrecognized EOF found at {}", location));
127+
fmt_expected(f, expected)
128+
}
129+
UnrecognizedToken { token: (ref start, ref token, ref end), ref expected } => {
130+
try!(write!(f, "Unrecognized token `{}` found at {}:{}", token, start, end));
131+
fmt_expected(f, expected)
132+
}
133+
ExtraToken { token: (ref start, ref token, ref end), } => {
134+
write!(f, "Extra token {} found at {}:{}", token, start, end)
122135
}
123-
ExtraToken {
124-
token: (ref start, ref token, ref end),
125-
} => write!(f, "Extra token {} found at {}:{}", token, start, end),
126-
User { ref error } => write!(f, "{}", error),
127136
}
128137
}
129138
}
@@ -189,7 +198,7 @@ mod tests {
189198
#[test]
190199
fn test() {
191200
let err = ParseError::UnrecognizedToken::<i32, &str, &str> {
192-
token: Some((1, "t0", 2)),
201+
token: (1, "t0", 2),
193202
expected: vec!["t1", "t2", "t3"]
194203
.into_iter()
195204
.map(|s| s.to_string())

lalrpop-util/src/state_machine.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -599,9 +599,19 @@ where
599599
token: Option<TokenTriple<D>>,
600600
top_state: D::StateIndex,
601601
) -> ParseError<D> {
602-
::ParseError::UnrecognizedToken {
603-
token: token,
604-
expected: self.definition.expected_tokens(top_state),
602+
match token {
603+
Some(token) => {
604+
::ParseError::UnrecognizedToken {
605+
token: token,
606+
expected: self.definition.expected_tokens(top_state),
607+
}
608+
}
609+
None => {
610+
::ParseError::UnrecognizedEOF {
611+
location: self.last_location.clone(),
612+
expected: self.definition.expected_tokens(top_state),
613+
}
614+
}
605615
}
606616
}
607617

lalrpop/src/build/mod.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -206,20 +206,19 @@ fn parse_and_normalize_grammar(session: &Session, file_text: &FileText) -> io::R
206206
);
207207
}
208208

209-
Err(ParseError::UnrecognizedToken {
210-
token: None,
209+
Err(ParseError::UnrecognizedEOF {
210+
location,
211211
expected: _,
212212
}) => {
213-
let len = file_text.text().len();
214213
report_error(
215214
&file_text,
216-
pt::Span(len, len),
215+
pt::Span(location, location),
217216
&format!("unexpected end of file"),
218217
);
219218
}
220219

221220
Err(ParseError::UnrecognizedToken {
222-
token: Some((lo, _, hi)),
221+
token: (lo, _, hi),
223222
expected,
224223
}) => {
225224
let _ = expected; // didn't implement this yet :)

lalrpop/src/lr1/codegen/ascent.rs

+43-9
Original file line numberDiff line numberDiff line change
@@ -374,20 +374,54 @@ impl<'ascent, 'grammar, W: Write>
374374
.iter()
375375
.any(|&(ref t, _)| t.contains(&Token::Terminal(terminal.clone())))
376376
});
377-
rust!(
378-
self.out,
379-
"return Err({}lalrpop_util::ParseError::UnrecognizedToken {{",
380-
self.prefix
381-
);
382-
rust!(self.out, "token: {}lookahead,", self.prefix);
383-
rust!(self.out, "expected: vec![");
377+
378+
rust!(self.out, "let {}expected = vec![", self.prefix);
384379
for terminal in successful_terminals {
385380
rust!(self.out, "r###\"{}\"###.to_string(),", terminal);
386381
}
387-
rust!(self.out, "]");
388-
rust!(self.out, "}});");
382+
rust!(self.out, "];");
383+
384+
// check if we've found an unrecognized token or EOF
385+
rust!(self.out, "return Err(");
386+
rust!(self.out, "match {}lookahead {{", self.prefix);
387+
388+
rust!(self.out, "Some({}token) => {{", self.prefix);
389+
rust!(self.out, "{}lalrpop_util::ParseError::UnrecognizedToken {{", self.prefix);
390+
rust!(self.out, "token: {}token,", self.prefix);
391+
rust!(self.out, "expected: {}expected,", self.prefix);
392+
rust!(self.out, "}}");
393+
rust!(self.out, "}}");
394+
395+
rust!(self.out, "None => {{");
396+
397+
// find the location of the last symbol on stack
398+
let (optional, fixed) = stack_suffix.optional_fixed_lens();
399+
if fixed > 0 {
400+
rust!(self.out, "let {}location = {}sym{}.2.clone();", self.prefix, self.prefix, stack_suffix.len() - 1);
401+
} else if optional > 0 {
402+
rust!(self.out, "let {}location = ", self.prefix);
403+
for index in (0..optional).rev() {
404+
rust!(self.out, "{}sym{}.as_ref().map(|sym| sym.2.clone()).unwrap_or_else(|| {{", self.prefix, index);
405+
}
406+
rust!(self.out, "Default::default()");
407+
for _ in 0..optional {
408+
rust!(self.out, "}})");
409+
}
410+
rust!(self.out, ";");
411+
} else {
412+
rust!(self.out, "let {}location = Default::default();", self.prefix);
413+
}
414+
415+
rust!(self.out, "{}lalrpop_util::ParseError::UnrecognizedEOF {{", self.prefix);
416+
rust!(self.out, "location: {}location,", self.prefix);
417+
rust!(self.out, "expected: {}expected,", self.prefix);
389418
rust!(self.out, "}}");
419+
rust!(self.out, "}}");
420+
421+
rust!(self.out, "}}"); // Error match
422+
rust!(self.out, ")");
390423

424+
rust!(self.out, "}}"); // Wildcard match case
391425
rust!(self.out, "}}"); // match
392426

393427
// finally, emit gotos (if relevant)

0 commit comments

Comments
 (0)