Add source_path to ParseError

This commit is contained in:
Jeong YunWon 2022-08-22 06:48:47 +09:00
parent cf10e800a6
commit 03ccd4b9d8
7 changed files with 55 additions and 49 deletions

View file

@ -2707,7 +2707,7 @@ mod tests {
"source_path".to_owned(), "source_path".to_owned(),
"<module>".to_owned(), "<module>".to_owned(),
); );
let ast = parser::parse_program(source).unwrap(); let ast = parser::parse_program(source, "<test>").unwrap();
let symbol_scope = SymbolTable::scan_program(&ast).unwrap(); let symbol_scope = SymbolTable::scan_program(&ast).unwrap();
compiler.compile_program(&ast, symbol_scope).unwrap(); compiler.compile_program(&ast, symbol_scope).unwrap();
compiler.pop_code_object() compiler.pop_code_object()

View file

@ -124,6 +124,7 @@ impl From<FStringError> for LalrpopError<Location, Tok, LexicalError> {
pub struct ParseError { pub struct ParseError {
pub error: ParseErrorType, pub error: ParseErrorType,
pub location: Location, pub location: Location,
pub source_path: String,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -141,21 +142,28 @@ pub enum ParseErrorType {
} }
/// Convert `lalrpop_util::ParseError` to our internal type /// Convert `lalrpop_util::ParseError` to our internal type
impl From<LalrpopError<Location, Tok, LexicalError>> for ParseError { impl ParseError {
fn from(err: LalrpopError<Location, Tok, LexicalError>) -> Self { pub(crate) fn from_lalrpop(
err: LalrpopError<Location, Tok, LexicalError>,
source_path: &str,
) -> Self {
let source_path = source_path.to_owned();
match err { match err {
// TODO: Are there cases where this isn't an EOF? // TODO: Are there cases where this isn't an EOF?
LalrpopError::InvalidToken { location } => ParseError { LalrpopError::InvalidToken { location } => ParseError {
error: ParseErrorType::Eof, error: ParseErrorType::Eof,
location, location,
source_path,
}, },
LalrpopError::ExtraToken { token } => ParseError { LalrpopError::ExtraToken { token } => ParseError {
error: ParseErrorType::ExtraToken(token.1), error: ParseErrorType::ExtraToken(token.1),
location: token.0, location: token.0,
source_path,
}, },
LalrpopError::User { error } => ParseError { LalrpopError::User { error } => ParseError {
error: ParseErrorType::Lexical(error.error), error: ParseErrorType::Lexical(error.error),
location: error.location, location: error.location,
source_path,
}, },
LalrpopError::UnrecognizedToken { token, expected } => { LalrpopError::UnrecognizedToken { token, expected } => {
// Hacky, but it's how CPython does it. See PyParser_AddToken, // Hacky, but it's how CPython does it. See PyParser_AddToken,
@ -164,11 +172,13 @@ impl From<LalrpopError<Location, Tok, LexicalError>> for ParseError {
ParseError { ParseError {
error: ParseErrorType::UnrecognizedToken(token.1, expected), error: ParseErrorType::UnrecognizedToken(token.1, expected),
location: token.0, location: token.0,
source_path,
} }
} }
LalrpopError::UnrecognizedEOF { location, .. } => ParseError { LalrpopError::UnrecognizedEOF { location, .. } => ParseError {
error: ParseErrorType::Eof, error: ParseErrorType::Eof,
location, location,
source_path,
}, },
} }
} }

View file

@ -293,7 +293,7 @@ impl<'a> FStringParser<'a> {
fn parse_fstring_expr(source: &str) -> Result<Expr, ParseError> { fn parse_fstring_expr(source: &str) -> Result<Expr, ParseError> {
let fstring_body = format!("({})", source); let fstring_body = format!("({})", source);
parse_expression(&fstring_body) parse_expression(&fstring_body, "<fstring>")
} }
/// Parse an fstring from a string, located at a certain position in the sourcecode. /// Parse an fstring from a string, located at a certain position in the sourcecode.

View file

@ -11,7 +11,7 @@
//! use rustpython_parser::{parser, ast}; //! use rustpython_parser::{parser, ast};
//! //!
//! let python_source = "print('Hello world')"; //! let python_source = "print('Hello world')";
//! let python_ast = parser::parse_expression(python_source).unwrap(); //! let python_ast = parser::parse_expression(python_source, "<embedded>").unwrap();
//! //!
//! ``` //! ```

View file

@ -20,8 +20,8 @@ use crate::python;
*/ */
/// Parse a full python program, containing usually multiple lines. /// Parse a full python program, containing usually multiple lines.
pub fn parse_program(source: &str) -> Result<ast::Suite, ParseError> { pub fn parse_program(source: &str, source_path: &str) -> Result<ast::Suite, ParseError> {
parse(source, Mode::Module).map(|top| match top { parse(source, Mode::Module, source_path).map(|top| match top {
ast::Mod::Module { body, .. } => body, ast::Mod::Module { body, .. } => body,
_ => unreachable!(), _ => unreachable!(),
}) })
@ -33,7 +33,7 @@ pub fn parse_program(source: &str) -> Result<ast::Suite, ParseError> {
/// ``` /// ```
/// extern crate num_bigint; /// extern crate num_bigint;
/// use rustpython_parser::{parser, ast}; /// use rustpython_parser::{parser, ast};
/// let expr = parser::parse_expression("1 + 2").unwrap(); /// let expr = parser::parse_expression("1 + 2", "<embedded>").unwrap();
/// ///
/// assert_eq!( /// assert_eq!(
/// expr, /// expr,
@ -63,22 +63,22 @@ pub fn parse_program(source: &str) -> Result<ast::Suite, ParseError> {
/// ); /// );
/// ///
/// ``` /// ```
pub fn parse_expression(source: &str) -> Result<ast::Expr, ParseError> { pub fn parse_expression(source: &str, path: &str) -> Result<ast::Expr, ParseError> {
parse(source, Mode::Expression).map(|top| match top { parse(source, Mode::Expression, path).map(|top| match top {
ast::Mod::Expression { body } => *body, ast::Mod::Expression { body } => *body,
_ => unreachable!(), _ => unreachable!(),
}) })
} }
// Parse a given source code // Parse a given source code
pub fn parse(source: &str, mode: Mode) -> Result<ast::Mod, ParseError> { pub fn parse(source: &str, mode: Mode, source_path: &str) -> Result<ast::Mod, ParseError> {
let lxr = lexer::make_tokenizer(source); let lxr = lexer::make_tokenizer(source);
let marker_token = (Default::default(), mode.to_marker(), Default::default()); let marker_token = (Default::default(), mode.to_marker(), Default::default());
let tokenizer = iter::once(Ok(marker_token)).chain(lxr); let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
python::TopParser::new() python::TopParser::new()
.parse(tokenizer) .parse(tokenizer)
.map_err(ParseError::from) .map_err(|e| ParseError::from_lalrpop(e, source_path))
} }
#[cfg(test)] #[cfg(test)]
@ -87,56 +87,56 @@ mod tests {
#[test] #[test]
fn test_parse_empty() { fn test_parse_empty() {
let parse_ast = parse_program("").unwrap(); let parse_ast = parse_program("", "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_string() { fn test_parse_string() {
let source = String::from("'Hello world'"); let source = String::from("'Hello world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_f_string() { fn test_parse_f_string() {
let source = String::from("f'Hello world'"); let source = String::from("f'Hello world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_print_hello() { fn test_parse_print_hello() {
let source = String::from("print('Hello world')"); let source = String::from("print('Hello world')");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_print_2() { fn test_parse_print_2() {
let source = String::from("print('Hello world', 2)"); let source = String::from("print('Hello world', 2)");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_kwargs() { fn test_parse_kwargs() {
let source = String::from("my_func('positional', keyword=2)"); let source = String::from("my_func('positional', keyword=2)");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_if_elif_else() { fn test_parse_if_elif_else() {
let source = String::from("if 1: 10\nelif 2: 20\nelse: 30"); let source = String::from("if 1: 10\nelif 2: 20\nelse: 30");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_lambda() { fn test_parse_lambda() {
let source = "lambda x, y: x * y"; // lambda(x, y): x * y"; let source = "lambda x, y: x * y"; // lambda(x, y): x * y";
let parse_ast = parse_program(source).unwrap(); let parse_ast = parse_program(source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
@ -144,7 +144,7 @@ mod tests {
fn test_parse_tuples() { fn test_parse_tuples() {
let source = "a, b = 4, 5"; let source = "a, b = 4, 5";
insta::assert_debug_snapshot!(parse_program(source).unwrap()); insta::assert_debug_snapshot!(parse_program(source, "<test>").unwrap());
} }
#[test] #[test]
@ -155,48 +155,48 @@ class Foo(A, B):
pass pass
def method_with_default(self, arg='default'): def method_with_default(self, arg='default'):
pass"; pass";
insta::assert_debug_snapshot!(parse_program(source).unwrap()); insta::assert_debug_snapshot!(parse_program(source, "<test>").unwrap());
} }
#[test] #[test]
fn test_parse_dict_comprehension() { fn test_parse_dict_comprehension() {
let source = String::from("{x1: x2 for y in z}"); let source = String::from("{x1: x2 for y in z}");
let parse_ast = parse_expression(&source).unwrap(); let parse_ast = parse_expression(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_list_comprehension() { fn test_parse_list_comprehension() {
let source = String::from("[x for y in z]"); let source = String::from("[x for y in z]");
let parse_ast = parse_expression(&source).unwrap(); let parse_ast = parse_expression(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_double_list_comprehension() { fn test_parse_double_list_comprehension() {
let source = String::from("[x for y, y2 in z for a in b if a < 5 if a > 10]"); let source = String::from("[x for y, y2 in z for a in b if a < 5 if a > 10]");
let parse_ast = parse_expression(&source).unwrap(); let parse_ast = parse_expression(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_generator_comprehension() { fn test_parse_generator_comprehension() {
let source = String::from("(x for y in z)"); let source = String::from("(x for y in z)");
let parse_ast = parse_expression(&source).unwrap(); let parse_ast = parse_expression(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_named_expression_generator_comprehension() { fn test_parse_named_expression_generator_comprehension() {
let source = String::from("(x := y + 1 for y in z)"); let source = String::from("(x := y + 1 for y in z)");
let parse_ast = parse_expression(&source).unwrap(); let parse_ast = parse_expression(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_if_else_generator_comprehension() { fn test_parse_if_else_generator_comprehension() {
let source = String::from("(x if y else y for y in z)"); let source = String::from("(x if y else y for y in z)");
let parse_ast = parse_expression(&source).unwrap(); let parse_ast = parse_expression(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
} }

View file

@ -88,56 +88,56 @@ mod tests {
#[test] #[test]
fn test_parse_string_concat() { fn test_parse_string_concat() {
let source = String::from("'Hello ' 'world'"); let source = String::from("'Hello ' 'world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_u_string_concat_1() { fn test_parse_u_string_concat_1() {
let source = String::from("'Hello ' u'world'"); let source = String::from("'Hello ' u'world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_u_string_concat_2() { fn test_parse_u_string_concat_2() {
let source = String::from("u'Hello ' 'world'"); let source = String::from("u'Hello ' 'world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_f_string_concat_1() { fn test_parse_f_string_concat_1() {
let source = String::from("'Hello ' f'world'"); let source = String::from("'Hello ' f'world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_f_string_concat_2() { fn test_parse_f_string_concat_2() {
let source = String::from("'Hello ' f'world'"); let source = String::from("'Hello ' f'world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_f_string_concat_3() { fn test_parse_f_string_concat_3() {
let source = String::from("'Hello ' f'world{\"!\"}'"); let source = String::from("'Hello ' f'world{\"!\"}'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_u_f_string_concat_1() { fn test_parse_u_f_string_concat_1() {
let source = String::from("u'Hello ' f'world'"); let source = String::from("u'Hello ' f'world'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_u_f_string_concat_2() { fn test_parse_u_f_string_concat_2() {
let source = String::from("u'Hello ' f'world' '!'"); let source = String::from("u'Hello ' f'world' '!'");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
} }

View file

@ -43,22 +43,18 @@ impl fmt::Display for CompileError {
impl CompileError { impl CompileError {
fn from_codegen(error: rustpython_codegen::error::CodegenError, source: &str) -> Self { fn from_codegen(error: rustpython_codegen::error::CodegenError, source: &str) -> Self {
CompileError { Self {
error: error.error.into(), error: error.error.into(),
location: error.location, location: error.location,
source_path: error.source_path, source_path: error.source_path,
statement: get_statement(source, error.location), statement: get_statement(source, error.location),
} }
} }
fn from_parse( fn from_parse(error: rustpython_parser::error::ParseError, source: &str) -> Self {
error: rustpython_parser::error::ParseError, Self {
source: &str,
source_path: String,
) -> Self {
CompileError {
error: error.error.into(), error: error.error.into(),
location: error.location, location: error.location,
source_path, source_path: error.source_path,
statement: get_statement(source, error.location), statement: get_statement(source, error.location),
} }
} }
@ -83,9 +79,9 @@ pub fn compile(
compile::Mode::Eval => parser::Mode::Expression, compile::Mode::Eval => parser::Mode::Expression,
compile::Mode::Single | compile::Mode::BlockExpr => parser::Mode::Interactive, compile::Mode::Single | compile::Mode::BlockExpr => parser::Mode::Interactive,
}; };
let mut ast = match parser::parse(source, parser_mode) { let mut ast = match parser::parse(source, parser_mode, &source_path) {
Ok(x) => x, Ok(x) => x,
Err(e) => return Err(CompileError::from_parse(e, source, source_path)), Err(e) => return Err(CompileError::from_parse(e, source)),
}; };
if opts.optimize > 0 { if opts.optimize > 0 {
ast = ConstantOptimizer::new() ast = ConstantOptimizer::new()
@ -101,14 +97,14 @@ pub fn compile_symtable(
mode: compile::Mode, mode: compile::Mode,
source_path: &str, source_path: &str,
) -> Result<symboltable::SymbolTable, CompileError> { ) -> Result<symboltable::SymbolTable, CompileError> {
let parse_err = |e| CompileError::from_parse(e, source, source_path.to_owned()); let parse_err = |e| CompileError::from_parse(e, source);
let res = match mode { let res = match mode {
compile::Mode::Exec | compile::Mode::Single | compile::Mode::BlockExpr => { compile::Mode::Exec | compile::Mode::Single | compile::Mode::BlockExpr => {
let ast = parser::parse_program(source).map_err(parse_err)?; let ast = parser::parse_program(source, source_path).map_err(parse_err)?;
symboltable::SymbolTable::scan_program(&ast) symboltable::SymbolTable::scan_program(&ast)
} }
compile::Mode::Eval => { compile::Mode::Eval => {
let expr = parser::parse_expression(source).map_err(parse_err)?; let expr = parser::parse_expression(source, source_path).map_err(parse_err)?;
symboltable::SymbolTable::scan_expr(&expr) symboltable::SymbolTable::scan_expr(&expr)
} }
}; };