Skip to content

Commit 4ca3d3b

Browse files
Merge remote-tracking branch 'upstream/master' into fstrings
2 parents a97e05a + 33d272f commit 4ca3d3b

File tree

13 files changed

+326
-190
lines changed

13 files changed

+326
-190
lines changed

parser/src/error.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! Define internal parse error types
2+
//! The goal is to provide a matching and a safe error API, maksing errors from LALR
3+
extern crate lalrpop_util;
4+
use self::lalrpop_util::ParseError as InnerError;
5+
6+
use lexer::{LexicalError, Location};
7+
use token::Tok;
8+
9+
use std::error::Error;
10+
use std::fmt;
11+
12+
// A token of type `Tok` was observed, with a span given by the two Location values
13+
type TokSpan = (Location, Tok, Location);
14+
15+
/// Represents an error during parsing
16+
#[derive(Debug, PartialEq)]
17+
pub enum ParseError {
18+
/// Parser encountered an unexpected end of input
19+
EOF(Option<Location>),
20+
/// Parser encountered an extra token
21+
ExtraToken(TokSpan),
22+
/// Parser encountered an invalid token
23+
InvalidToken(Location),
24+
/// Parser encountered an unexpected token
25+
UnrecognizedToken(TokSpan, Vec<String>),
26+
/// Maps to `User` type from `lalrpop-util`
27+
Other,
28+
}
29+
30+
/// Convert `lalrpop_util::ParseError` to our internal type
31+
impl From<InnerError<Location, Tok, LexicalError>> for ParseError {
32+
fn from(err: InnerError<Location, Tok, LexicalError>) -> Self {
33+
match err {
34+
// TODO: Are there cases where this isn't an EOF?
35+
InnerError::InvalidToken { location } => ParseError::EOF(Some(location)),
36+
InnerError::ExtraToken { token } => ParseError::ExtraToken(token),
37+
// Inner field is a unit-like enum `LexicalError::StringError` with no useful info
38+
InnerError::User { .. } => ParseError::Other,
39+
InnerError::UnrecognizedToken { token, expected } => {
40+
match token {
41+
Some(tok) => ParseError::UnrecognizedToken(tok, expected),
42+
// EOF was observed when it was unexpected
43+
None => ParseError::EOF(None),
44+
}
45+
}
46+
}
47+
}
48+
}
49+
50+
impl fmt::Display for ParseError {
51+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52+
match *self {
53+
ParseError::EOF(ref location) => {
54+
if let Some(l) = location {
55+
write!(f, "Got unexpected EOF at: {:?}", l)
56+
} else {
57+
write!(f, "Got unexpected EOF")
58+
}
59+
}
60+
ParseError::ExtraToken(ref t_span) => {
61+
write!(f, "Got extraneous token: {:?} at: {:?}", t_span.1, t_span.0)
62+
}
63+
ParseError::InvalidToken(ref location) => {
64+
write!(f, "Got invalid token at: {:?}", location)
65+
}
66+
ParseError::UnrecognizedToken(ref t_span, _) => {
67+
write!(f, "Got unexpected token: {:?} at {:?}", t_span.1, t_span.0)
68+
}
69+
// This is user defined, it probably means a more useful error should have been given upstream.
70+
ParseError::Other => write!(f, "Got unsupported token(s)"),
71+
}
72+
}
73+
}
74+
75+
impl Error for ParseError {
76+
fn source(&self) -> Option<&(dyn Error + 'static)> {
77+
None
78+
}
79+
}

parser/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ extern crate num_bigint;
55
extern crate num_traits;
66

77
pub mod ast;
8+
pub mod error;
89
pub mod lexer;
910
pub mod parser;
1011
#[cfg_attr(rustfmt, rustfmt_skip)]
1112
mod python;
1213
pub mod token;
13-
14-
pub use self::parser::parse;

parser/src/parser.rs

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,28 @@
11
extern crate lalrpop_util;
22

3-
use std::error::Error;
4-
use std::fs::File;
5-
use std::io::Read;
63
use std::iter;
74
use std::mem;
8-
use std::path::Path;
9-
10-
use self::lalrpop_util::ParseError;
115

126
use super::ast;
7+
use super::error::ParseError;
138
use super::lexer;
149
use super::python;
1510
use super::token;
1611

17-
pub fn read_file(filename: &Path) -> Result<String, String> {
18-
info!("Loading file {:?}", filename);
19-
match File::open(&filename) {
20-
Ok(mut file) => {
21-
let mut s = String::new();
22-
23-
match file.read_to_string(&mut s) {
24-
Err(why) => Err(String::from("Reading file failed: ") + why.description()),
25-
Ok(_) => Ok(s),
26-
}
27-
}
28-
Err(why) => Err(String::from("Opening file failed: ") + why.description()),
29-
}
30-
}
31-
3212
/*
3313
* Parse python code.
3414
* Grammar may be inspired by antlr grammar for python:
3515
* https://github.com/antlr/grammars-v4/tree/master/python3
3616
*/
3717

38-
pub fn parse(filename: &Path) -> Result<ast::Program, String> {
39-
info!("Parsing: {}", filename.display());
40-
let txt = read_file(filename)?;
41-
debug!("Read contents of file: {}", txt);
42-
parse_program(&txt)
43-
}
44-
4518
macro_rules! do_lalr_parsing {
4619
($input: expr, $pat: ident, $tok: ident) => {{
4720
let lxr = lexer::make_tokenizer($input);
4821
let marker_token = (Default::default(), token::Tok::$tok, Default::default());
4922
let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
5023

5124
match python::TopParser::new().parse(tokenizer) {
52-
Err(why) => Err(format!("{:?}", why)),
25+
Err(err) => Err(ParseError::from(err)),
5326
Ok(top) => {
5427
if let ast::Top::$pat(x) = top {
5528
Ok(x)
@@ -61,11 +34,11 @@ macro_rules! do_lalr_parsing {
6134
}};
6235
}
6336

64-
pub fn parse_program(source: &str) -> Result<ast::Program, String> {
37+
pub fn parse_program(source: &str) -> Result<ast::Program, ParseError> {
6538
do_lalr_parsing!(source, Program, StartProgram)
6639
}
6740

68-
pub fn parse_statement(source: &str) -> Result<ast::LocatedStatement, String> {
41+
pub fn parse_statement(source: &str) -> Result<ast::LocatedStatement, ParseError> {
6942
do_lalr_parsing!(source, Statement, StartStatement)
7043
}
7144

@@ -91,21 +64,22 @@ pub fn parse_statement(source: &str) -> Result<ast::LocatedStatement, String> {
9164
/// expr);
9265
///
9366
/// ```
94-
pub fn parse_expression(source: &str) -> Result<ast::Expression, String> {
67+
pub fn parse_expression(source: &str) -> Result<ast::Expression, ParseError> {
9568
do_lalr_parsing!(source, Expression, StartExpression)
9669
}
9770

71+
// TODO: consolidate these with ParseError
9872
pub enum FStringError {
9973
UnclosedLbrace,
10074
UnopenedRbrace,
10175
InvalidExpression,
10276
}
10377

104-
impl From<FStringError> for ParseError<lexer::Location, token::Tok, lexer::LexicalError> {
78+
impl From<FStringError>
79+
for lalrpop_util::ParseError<lexer::Location, token::Tok, lexer::LexicalError>
80+
{
10581
fn from(_err: FStringError) -> Self {
106-
// TODO: we should have our own top-level ParseError to properly propagate f-string (and
107-
// other) syntax errors
108-
ParseError::User {
82+
lalrpop_util::ParseError::User {
10983
error: lexer::LexicalError::StringError,
11084
}
11185
}
@@ -196,7 +170,6 @@ mod tests {
196170
#[test]
197171
fn test_parse_empty() {
198172
let parse_ast = parse_program(&String::from("\n"));
199-
200173
assert_eq!(parse_ast, Ok(ast::Program { statements: vec![] }))
201174
}
202175

0 commit comments

Comments
 (0)