Skip to content

Commit 70c2054

Browse files
Merge pull request RustPython#1106 from palaviv/syntex-error
Pass more information in user defined parse error
2 parents a099ba5 + 0be08fe commit 70c2054

File tree

5 files changed

+83
-67
lines changed

5 files changed

+83
-67
lines changed

compiler/src/error.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustpython_parser::error::ParseError;
1+
use rustpython_parser::error::{ParseError, ParseErrorType};
22
use rustpython_parser::lexer::Location;
33

44
use std::error::Error;
@@ -13,8 +13,8 @@ pub struct CompileError {
1313
impl From<ParseError> for CompileError {
1414
fn from(error: ParseError) -> Self {
1515
CompileError {
16-
error: CompileErrorType::Parse(error),
17-
location: Default::default(), // TODO: extract location from parse error!
16+
error: CompileErrorType::Parse(error.error),
17+
location: error.location,
1818
}
1919
}
2020
}
@@ -28,7 +28,7 @@ pub enum CompileErrorType {
2828
/// Expected an expression got a statement
2929
ExpectExpr,
3030
/// Parser error
31-
Parse(ParseError),
31+
Parse(ParseErrorType),
3232
SyntaxError(String),
3333
/// Multiple `*` detected
3434
StarArgs,
@@ -56,7 +56,7 @@ impl fmt::Display for CompileError {
5656
}?;
5757

5858
// Print line number:
59-
write!(f, " at line {:?}", self.location.row())
59+
write!(f, " at {}", self.location)
6060
}
6161
}
6262

parser/src/error.rs

+45-33
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,83 @@
33
extern crate lalrpop_util;
44
use self::lalrpop_util::ParseError as InnerError;
55

6-
use crate::lexer::{LexicalError, Location};
6+
use crate::lexer::{LexicalError, LexicalErrorType, Location};
77
use crate::token::Tok;
88

99
use std::error::Error;
1010
use std::fmt;
1111

12-
// A token of type `Tok` was observed, with a span given by the two Location values
13-
type TokSpan = (Location, Tok, Location);
14-
1512
/// Represents an error during parsing
1613
#[derive(Debug, PartialEq)]
17-
pub enum ParseError {
14+
pub struct ParseError {
15+
pub error: ParseErrorType,
16+
pub location: Location,
17+
}
18+
19+
#[derive(Debug, PartialEq)]
20+
pub enum ParseErrorType {
1821
/// Parser encountered an unexpected end of input
19-
EOF(Option<Location>),
22+
EOF,
2023
/// Parser encountered an extra token
21-
ExtraToken(TokSpan),
24+
ExtraToken(Tok),
2225
/// Parser encountered an invalid token
23-
InvalidToken(Location),
26+
InvalidToken,
2427
/// Parser encountered an unexpected token
25-
UnrecognizedToken(TokSpan, Vec<String>),
28+
UnrecognizedToken(Tok, Vec<String>),
2629
/// Maps to `User` type from `lalrpop-util`
27-
Other,
30+
Lexical(LexicalErrorType),
2831
}
2932

3033
/// Convert `lalrpop_util::ParseError` to our internal type
3134
impl From<InnerError<Location, Tok, LexicalError>> for ParseError {
3235
fn from(err: InnerError<Location, Tok, LexicalError>) -> Self {
3336
match err {
3437
// 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,
38+
InnerError::InvalidToken { location } => ParseError {
39+
error: ParseErrorType::EOF,
40+
location,
41+
},
42+
InnerError::ExtraToken { token } => ParseError {
43+
error: ParseErrorType::ExtraToken(token.1),
44+
location: token.0,
45+
},
46+
InnerError::User { error } => ParseError {
47+
error: ParseErrorType::Lexical(error.error),
48+
location: error.location,
49+
},
3950
InnerError::UnrecognizedToken { token, expected } => {
4051
match token {
41-
Some(tok) => ParseError::UnrecognizedToken(tok, expected),
52+
Some(tok) => ParseError {
53+
error: ParseErrorType::UnrecognizedToken(tok.1, expected),
54+
location: tok.0,
55+
},
4256
// EOF was observed when it was unexpected
43-
None => ParseError::EOF(None),
57+
None => ParseError {
58+
error: ParseErrorType::EOF,
59+
location: Default::default(),
60+
},
4461
}
4562
}
4663
}
4764
}
4865
}
4966

5067
impl fmt::Display for ParseError {
68+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69+
write!(f, "{} at {}", self.error, self.location)
70+
}
71+
}
72+
73+
impl fmt::Display for ParseErrorType {
5174
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5275
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)
76+
ParseErrorType::EOF => write!(f, "Got unexpected EOF"),
77+
ParseErrorType::ExtraToken(ref tok) => write!(f, "Got extraneous token: {:?}", tok),
78+
ParseErrorType::InvalidToken => write!(f, "Got invalid token"),
79+
ParseErrorType::UnrecognizedToken(ref tok, _) => {
80+
write!(f, "Got unexpected token: {:?}", tok)
6881
}
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)"),
82+
ParseErrorType::Lexical(ref error) => write!(f, "{}", error),
7183
}
7284
}
7385
}

parser/src/lexer.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use num_traits::Num;
1010
use serde::{Deserialize, Serialize};
1111
use std::cmp::Ordering;
1212
use std::collections::HashMap;
13+
use std::fmt;
1314
use std::str::FromStr;
1415
use unic_emoji_char::is_emoji_presentation;
1516
use unicode_xid::UnicodeXID;
@@ -59,13 +60,13 @@ pub struct Lexer<T: Iterator<Item = char>> {
5960
keywords: HashMap<String, Tok>,
6061
}
6162

62-
#[derive(Debug)]
63+
#[derive(Debug, PartialEq)]
6364
pub struct LexicalError {
6465
pub error: LexicalErrorType,
6566
pub location: Location,
6667
}
6768

68-
#[derive(Debug)]
69+
#[derive(Debug, PartialEq)]
6970
pub enum LexicalErrorType {
7071
StringError,
7172
UnicodeError,
@@ -74,12 +75,32 @@ pub enum LexicalErrorType {
7475
OtherError(String),
7576
}
7677

78+
impl fmt::Display for LexicalErrorType {
79+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80+
match *self {
81+
LexicalErrorType::StringError => write!(f, "Got unexpected string"),
82+
LexicalErrorType::UnicodeError => write!(f, "Got unexpected unicode"),
83+
LexicalErrorType::NestingError => write!(f, "Got unexpected nesting"),
84+
LexicalErrorType::UnrecognizedToken { tok } => {
85+
write!(f, "Got unexpected token {}", tok)
86+
}
87+
LexicalErrorType::OtherError(ref msg) => write!(f, "{}", msg),
88+
}
89+
}
90+
}
91+
7792
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
7893
pub struct Location {
7994
row: usize,
8095
column: usize,
8196
}
8297

98+
impl fmt::Display for Location {
99+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100+
write!(f, "line {} column {}", self.row, self.column)
101+
}
102+
}
103+
83104
impl Location {
84105
pub fn new(row: usize, column: usize) -> Self {
85106
Location { row, column }

src/main.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ extern crate rustyline;
77

88
use clap::{App, Arg};
99
use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType};
10-
use rustpython_parser::error::ParseError;
10+
use rustpython_parser::error::ParseErrorType;
1111
use rustpython_vm::{
1212
frame::Scope,
1313
import,
@@ -181,7 +181,7 @@ fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), Com
181181
// Don't inject syntax errors for line continuation
182182
Err(
183183
err @ CompileError {
184-
error: CompileErrorType::Parse(ParseError::EOF(_)),
184+
error: CompileErrorType::Parse(ParseErrorType::EOF),
185185
..
186186
},
187187
) => Err(err),
@@ -258,7 +258,7 @@ fn run_shell(vm: &VirtualMachine) -> PyResult {
258258

259259
match shell_exec(vm, &input, vars.clone()) {
260260
Err(CompileError {
261-
error: CompileErrorType::Parse(ParseError::EOF(_)),
261+
error: CompileErrorType::Parse(ParseErrorType::EOF),
262262
..
263263
}) => {
264264
continuing = true;

wasm/lib/src/vm_class.rs

+7-24
Original file line numberDiff line numberDiff line change
@@ -281,30 +281,13 @@ impl WASMVirtualMachine {
281281
let code = vm.compile(&source, &mode, "<wasm>".to_string());
282282
let code = code.map_err(|err| {
283283
let js_err = SyntaxError::new(&format!("Error parsing Python code: {}", err));
284-
if let CompileErrorType::Parse(ref parse_error) = err.error {
285-
use rustpython_parser::error::ParseError;
286-
if let ParseError::EOF(Some(ref loc))
287-
| ParseError::ExtraToken((ref loc, ..))
288-
| ParseError::InvalidToken(ref loc)
289-
| ParseError::UnrecognizedToken((ref loc, ..), _) = parse_error
290-
{
291-
let _ =
292-
Reflect::set(&js_err, &"row".into(), &(loc.row() as u32).into());
293-
let _ =
294-
Reflect::set(&js_err, &"col".into(), &(loc.column() as u32).into());
295-
}
296-
if let ParseError::ExtraToken((_, _, ref loc))
297-
| ParseError::UnrecognizedToken((_, _, ref loc), _) = parse_error
298-
{
299-
let _ =
300-
Reflect::set(&js_err, &"endrow".into(), &(loc.row() as u32).into());
301-
let _ = Reflect::set(
302-
&js_err,
303-
&"endcol".into(),
304-
&(loc.column() as u32).into(),
305-
);
306-
}
307-
}
284+
let _ =
285+
Reflect::set(&js_err, &"row".into(), &(err.location.row() as u32).into());
286+
let _ = Reflect::set(
287+
&js_err,
288+
&"col".into(),
289+
&(err.location.column() as u32).into(),
290+
);
308291
js_err
309292
})?;
310293
let result = vm.run_code_obj(code, scope.borrow().clone());

0 commit comments

Comments
 (0)