diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 424d85a..3d1a455 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -17,6 +17,7 @@ itertools = "0.10.3" log = "0.4.16" num-complex = { version = "0.4.0", features = ["serde"] } num-traits = "0.2.14" +thiserror = "1.0" [dev-dependencies] rustpython-parser = { path = "../parser" } diff --git a/codegen/src/error.rs b/codegen/src/error.rs index cfc4741..9073abb 100644 --- a/codegen/src/error.rs +++ b/codegen/src/error.rs @@ -1,8 +1,8 @@ -use std::{error::Error, fmt}; +use std::fmt; pub type CodegenError = rustpython_compiler_core::BaseError; -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum CodegenErrorType { /// Invalid assignment, cannot store value in target. @@ -79,5 +79,3 @@ impl fmt::Display for CodegenErrorType { } } } - -impl Error for CodegenErrorType {} diff --git a/core/src/error.rs b/core/src/error.rs index f69b61f..fde16d8 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -1,6 +1,5 @@ -use std::fmt::Display; - use crate::Location; +use std::fmt::Display; #[derive(Debug, PartialEq, Eq)] pub struct BaseError { @@ -87,3 +86,24 @@ where } } } + +impl CompileError { + pub fn from(error: BaseError, source: &str) -> Self + where + T: From, + { + let statement = get_statement(source, error.location); + CompileError { + body: error.into(), + statement, + } + } +} + +fn get_statement(source: &str, loc: Location) -> Option { + if loc.column() == 0 || loc.row() == 0 { + return None; + } + let line = source.split('\n').nth(loc.row() - 1)?.to_owned(); + Some(line + "\n") +} diff --git a/parser/src/error.rs b/parser/src/error.rs index 77f2f8f..4dd899e 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -117,8 +117,7 @@ impl From for LalrpopError { } /// Represents an error during parsing -#[derive(Debug, PartialEq)] -pub struct ParseError(rustpython_compiler_core::BaseError); +pub type ParseError = rustpython_compiler_core::BaseError; #[derive(Debug, PartialEq)] pub enum ParseErrorType { @@ -134,66 +133,44 @@ pub enum ParseErrorType { Lexical(LexicalErrorType), } -impl From for rustpython_compiler_core::BaseError { - fn from(err: ParseError) -> Self { - err.0 - } -} - -impl From for ParseErrorType { - fn from(err: ParseError) -> Self { - err.0.error - } -} - /// Convert `lalrpop_util::ParseError` to our internal type -impl ParseError { - fn new(error: ParseErrorType, location: Location, source_path: String) -> Self { - Self(rustpython_compiler_core::BaseError { - error, +pub(crate) fn parse_error_from_lalrpop( + err: LalrpopError, + source_path: &str, +) -> ParseError { + let source_path = source_path.to_owned(); + match err { + // TODO: Are there cases where this isn't an EOF? + LalrpopError::InvalidToken { location } => ParseError { + error: ParseErrorType::Eof, location, source_path, - }) - } - - pub(crate) fn from_lalrpop( - err: LalrpopError, - source_path: &str, - ) -> Self { - let source_path = source_path.to_owned(); - match err { - // TODO: Are there cases where this isn't an EOF? - LalrpopError::InvalidToken { location } => { - ParseError::new(ParseErrorType::Eof, location, source_path) - } - LalrpopError::ExtraToken { token } => { - ParseError::new(ParseErrorType::ExtraToken(token.1), token.0, source_path) - } - LalrpopError::User { error } => ParseError::new( - ParseErrorType::Lexical(error.error), - error.location, + }, + LalrpopError::ExtraToken { token } => ParseError { + error: ParseErrorType::ExtraToken(token.1), + location: token.0, + source_path, + }, + LalrpopError::User { error } => ParseError { + error: ParseErrorType::Lexical(error.error), + location: error.location, + source_path, + }, + LalrpopError::UnrecognizedToken { token, expected } => { + // Hacky, but it's how CPython does it. See PyParser_AddToken, + // in particular "Only one possible expected token" comment. + let expected = (expected.len() == 1).then(|| expected[0].clone()); + ParseError { + error: ParseErrorType::UnrecognizedToken(token.1, expected), + location: token.0, source_path, - ), - LalrpopError::UnrecognizedToken { token, expected } => { - // Hacky, but it's how CPython does it. See PyParser_AddToken, - // in particular "Only one possible expected token" comment. - let expected = (expected.len() == 1).then(|| expected[0].clone()); - ParseError::new( - ParseErrorType::UnrecognizedToken(token.1, expected), - token.0, - source_path, - ) - } - LalrpopError::UnrecognizedEOF { location, .. } => { - ParseError::new(ParseErrorType::Eof, location, source_path) } } - } -} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) + LalrpopError::UnrecognizedEOF { location, .. } => ParseError { + error: ParseErrorType::Eof, + location, + source_path, + }, } } @@ -237,16 +214,3 @@ impl ParseErrorType { ) } } - -impl std::ops::Deref for ParseError { - type Target = rustpython_compiler_core::BaseError; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Error for ParseError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - None - } -} diff --git a/parser/src/fstring.rs b/parser/src/fstring.rs index dc17063..11d75a0 100644 --- a/parser/src/fstring.rs +++ b/parser/src/fstring.rs @@ -186,7 +186,7 @@ impl<'a> FStringParser<'a> { vec![self.expr(ExprKind::FormattedValue { value: Box::new( parse_fstring_expr(&expression) - .map_err(|e| InvalidExpression(Box::new(e.into())))?, + .map_err(|e| InvalidExpression(Box::new(e.error)))?, ), conversion: conversion as _, format_spec: spec, @@ -204,7 +204,7 @@ impl<'a> FStringParser<'a> { self.expr(ExprKind::FormattedValue { value: Box::new( parse_fstring_expr(&expression) - .map_err(|e| InvalidExpression(Box::new(e.into())))?, + .map_err(|e| InvalidExpression(Box::new(e.error)))?, ), conversion: (if conversion == ConversionFlag::None && spec.is_none() { diff --git a/parser/src/parser.rs b/parser/src/parser.rs index ff86ef4..cd74856 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -74,7 +74,7 @@ pub fn parse(source: &str, mode: Mode, source_path: &str) -> Result; -fn error_from_codegen( - error: rustpython_codegen::error::CodegenError, - source: &str, -) -> CompileError { - let statement = get_statement(source, error.location); - CompileError { - body: error.into(), - statement, - } -} - fn error_from_parse(error: rustpython_parser::error::ParseError, source: &str) -> CompileError { let error: CompileErrorBody = error.into(); - let statement = get_statement(source, error.location); - CompileError { - body: error.into(), - statement, - } + CompileError::from(error, source) } /// Compile a given sourcecode into a bytecode object. @@ -60,7 +45,7 @@ pub fn compile( .fold_mod(ast) .unwrap_or_else(|e| match e {}); } - compile::compile_top(&ast, source_path, mode, opts).map_err(|e| error_from_codegen(e, source)) + compile::compile_top(&ast, source_path, mode, opts).map_err(|e| CompileError::from(e, source)) } pub fn compile_symtable( @@ -79,13 +64,5 @@ pub fn compile_symtable( symboltable::SymbolTable::scan_expr(&expr) } }; - res.map_err(|e| error_from_codegen(e.into_codegen_error(source_path.to_owned()), source)) -} - -fn get_statement(source: &str, loc: Location) -> Option { - if loc.column() == 0 || loc.row() == 0 { - return None; - } - let line = source.split('\n').nth(loc.row() - 1)?.to_owned(); - Some(line + "\n") + res.map_err(|e| CompileError::from(e.into_codegen_error(source_path.to_owned()), source)) }