Provide caret diagnostics for SyntaxError

visualize syntax error with caret diagnostics in shell, eval, exec,
when the error statement and error location are provided.
This commit is contained in:
yanganto 2019-11-21 17:04:32 +08:00
parent a51a3e0e4b
commit 28fbdffdc8
3 changed files with 42 additions and 14 deletions

View file

@ -255,6 +255,7 @@ impl<O: OutputStream> Compiler<O> {
self.compile_expression(expression)?; self.compile_expression(expression)?;
} else { } else {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::ExpectExpr, error: CompileErrorType::ExpectExpr,
location: statement.location.clone(), location: statement.location.clone(),
}); });
@ -533,6 +534,7 @@ impl<O: OutputStream> Compiler<O> {
Break => { Break => {
if !self.ctx.in_loop { if !self.ctx.in_loop {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::InvalidBreak, error: CompileErrorType::InvalidBreak,
location: statement.location.clone(), location: statement.location.clone(),
}); });
@ -542,6 +544,7 @@ impl<O: OutputStream> Compiler<O> {
Continue => { Continue => {
if !self.ctx.in_loop { if !self.ctx.in_loop {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::InvalidContinue, error: CompileErrorType::InvalidContinue,
location: statement.location.clone(), location: statement.location.clone(),
}); });
@ -551,6 +554,7 @@ impl<O: OutputStream> Compiler<O> {
Return { value } => { Return { value } => {
if !self.ctx.in_func() { if !self.ctx.in_func() {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::InvalidReturn, error: CompileErrorType::InvalidReturn,
location: statement.location.clone(), location: statement.location.clone(),
}); });
@ -628,6 +632,7 @@ impl<O: OutputStream> Compiler<O> {
} }
_ => { _ => {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::Delete(expression.name()), error: CompileErrorType::Delete(expression.name()),
location: self.current_source_location.clone(), location: self.current_source_location.clone(),
}); });
@ -1331,6 +1336,7 @@ impl<O: OutputStream> Compiler<O> {
if let ast::ExpressionType::Starred { .. } = &element.node { if let ast::ExpressionType::Starred { .. } = &element.node {
if seen_star { if seen_star {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::StarArgs, error: CompileErrorType::StarArgs,
location: self.current_source_location.clone(), location: self.current_source_location.clone(),
}); });
@ -1360,6 +1366,7 @@ impl<O: OutputStream> Compiler<O> {
} }
_ => { _ => {
return Err(CompileError { return Err(CompileError {
statement: None,
error: CompileErrorType::Assign(target.name()), error: CompileErrorType::Assign(target.name()),
location: self.current_source_location.clone(), location: self.current_source_location.clone(),
}); });
@ -1644,6 +1651,7 @@ impl<O: OutputStream> Compiler<O> {
Yield { value } => { Yield { value } => {
if !self.ctx.in_func() { if !self.ctx.in_func() {
return Err(CompileError { return Err(CompileError {
statement: Option::None,
error: CompileErrorType::InvalidYield, error: CompileErrorType::InvalidYield,
location: self.current_source_location.clone(), location: self.current_source_location.clone(),
}); });
@ -1738,6 +1746,7 @@ impl<O: OutputStream> Compiler<O> {
} }
Starred { .. } => { Starred { .. } => {
return Err(CompileError { return Err(CompileError {
statement: Option::None,
error: CompileErrorType::SyntaxError(std::string::String::from( error: CompileErrorType::SyntaxError(std::string::String::from(
"Invalid starred expression", "Invalid starred expression",
)), )),

View file

@ -7,13 +7,21 @@ use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct CompileError { pub struct CompileError {
pub statement: Option<String>,
pub error: CompileErrorType, pub error: CompileErrorType,
pub location: Location, pub location: Location,
} }
impl CompileError {
pub fn update_statement_info(&mut self, statement: String) {
self.statement = Some(statement);
}
}
impl From<ParseError> for CompileError { impl From<ParseError> for CompileError {
fn from(error: ParseError) -> Self { fn from(error: ParseError) -> Self {
CompileError { CompileError {
statement: None,
error: CompileErrorType::Parse(error.error), error: CompileErrorType::Parse(error.error),
location: error.location, location: error.location,
} }
@ -70,21 +78,31 @@ impl CompileError {
impl fmt::Display for CompileError { impl fmt::Display for CompileError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.error { let error_desc = match &self.error {
CompileErrorType::Assign(target) => write!(f, "can't assign to {}", target), CompileErrorType::Assign(target) => format!("can't assign to {}", target),
CompileErrorType::Delete(target) => write!(f, "can't delete {}", target), CompileErrorType::Delete(target) => format!("can't delete {}", target),
CompileErrorType::ExpectExpr => write!(f, "Expecting expression, got statement"), CompileErrorType::ExpectExpr => "Expecting expression, got statement".to_string(),
CompileErrorType::Parse(err) => write!(f, "{}", err), CompileErrorType::Parse(err) => err.to_string(),
CompileErrorType::SyntaxError(err) => write!(f, "{}", err), CompileErrorType::SyntaxError(err) => err.to_string(),
CompileErrorType::StarArgs => write!(f, "Two starred expressions in assignment"), CompileErrorType::StarArgs => "Two starred expressions in assignment".to_string(),
CompileErrorType::InvalidBreak => write!(f, "'break' outside loop"), CompileErrorType::InvalidBreak => "'break' outside loop".to_string(),
CompileErrorType::InvalidContinue => write!(f, "'continue' outside loop"), CompileErrorType::InvalidContinue => "'continue' outside loop".to_string(),
CompileErrorType::InvalidReturn => write!(f, "'return' outside function"), CompileErrorType::InvalidReturn => "'return' outside function".to_string(),
CompileErrorType::InvalidYield => write!(f, "'yield' outside function"), CompileErrorType::InvalidYield => "'yield' outside function".to_string(),
}?; };
// Print line number: if self.statement.is_some() && self.location.column() > 0 {
write!(f, " at {}", self.location) // visualize the error, when location and statement are provided
write!(
f,
"\n{}\n{}",
self.statement.clone().unwrap(),
self.location.visualize(&error_desc)
)
} else {
// print line number
write!(f, "{} at {}", error_desc, self.location)
}
} }
} }

View file

@ -142,6 +142,7 @@ pub struct SymbolTableError {
impl From<SymbolTableError> for CompileError { impl From<SymbolTableError> for CompileError {
fn from(error: SymbolTableError) -> Self { fn from(error: SymbolTableError) -> Self {
CompileError { CompileError {
statement: None,
error: CompileErrorType::SyntaxError(error.error), error: CompileErrorType::SyntaxError(error.error),
location: error.location, location: error.location,
} }