2021-08-10 07:08:10 +02:00
|
|
|
use core::ops::Range;
|
|
|
|
|
|
|
|
use crate::{ParseError, ParserWorkingSet, ShellError, Span};
|
|
|
|
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
|
|
|
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
|
|
|
|
|
|
|
impl<'a> codespan_reporting::files::Files<'a> for ParserWorkingSet<'a> {
|
|
|
|
type FileId = usize;
|
|
|
|
|
|
|
|
type Name = String;
|
|
|
|
|
|
|
|
type Source = String;
|
|
|
|
|
|
|
|
fn name(&'a self, id: Self::FileId) -> Result<Self::Name, codespan_reporting::files::Error> {
|
|
|
|
Ok(self.get_filename(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn source(
|
|
|
|
&'a self,
|
|
|
|
id: Self::FileId,
|
|
|
|
) -> Result<Self::Source, codespan_reporting::files::Error> {
|
|
|
|
Ok(self.get_file_source(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn line_index(
|
|
|
|
&'a self,
|
|
|
|
id: Self::FileId,
|
|
|
|
byte_index: usize,
|
|
|
|
) -> Result<usize, codespan_reporting::files::Error> {
|
|
|
|
let source = self.get_file_source(id);
|
|
|
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
|
|
|
for byte in source.bytes().enumerate() {
|
|
|
|
if byte.0 == byte_index {
|
|
|
|
// println!("count: {} for file: {} index: {}", count, id, byte_index);
|
|
|
|
return Ok(count);
|
|
|
|
}
|
|
|
|
if byte.1 == b'\n' {
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// println!("count: {} for file: {} index: {}", count, id, byte_index);
|
|
|
|
Ok(count)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn line_range(
|
|
|
|
&'a self,
|
|
|
|
id: Self::FileId,
|
|
|
|
line_index: usize,
|
|
|
|
) -> Result<Range<usize>, codespan_reporting::files::Error> {
|
|
|
|
let source = self.get_file_source(id);
|
|
|
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
|
|
|
let mut start = Some(0);
|
|
|
|
let mut end = None;
|
|
|
|
|
|
|
|
for byte in source.bytes().enumerate() {
|
|
|
|
#[allow(clippy::comparison_chain)]
|
|
|
|
if count > line_index {
|
|
|
|
let start = start.expect("internal error: couldn't find line");
|
|
|
|
let end = end.expect("internal error: couldn't find line");
|
|
|
|
|
|
|
|
// println!(
|
|
|
|
// "Span: {}..{} for fileid: {} index: {}",
|
|
|
|
// start, end, id, line_index
|
|
|
|
// );
|
|
|
|
return Ok(start..end);
|
|
|
|
} else if count == line_index {
|
|
|
|
end = Some(byte.0 + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::comparison_chain)]
|
|
|
|
if byte.1 == b'\n' {
|
|
|
|
count += 1;
|
|
|
|
if count > line_index {
|
|
|
|
break;
|
|
|
|
} else if count == line_index {
|
2021-08-10 07:55:25 +02:00
|
|
|
start = Some(byte.0 + 1);
|
2021-08-10 07:08:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match (start, end) {
|
|
|
|
(Some(start), Some(end)) => {
|
|
|
|
// println!(
|
|
|
|
// "Span: {}..{} for fileid: {} index: {}",
|
|
|
|
// start, end, id, line_index
|
|
|
|
// );
|
|
|
|
Ok(start..end)
|
|
|
|
}
|
|
|
|
_ => Err(codespan_reporting::files::Error::FileMissing),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn convert_span_to_diag(
|
|
|
|
working_set: &ParserWorkingSet,
|
|
|
|
span: &Span,
|
|
|
|
) -> Result<(usize, Range<usize>), Box<dyn std::error::Error>> {
|
|
|
|
for (file_id, (_, start, end)) in working_set.files().enumerate() {
|
|
|
|
if span.start >= *start && span.end <= *end {
|
|
|
|
let new_start = span.start - start;
|
|
|
|
let new_end = span.end - start;
|
|
|
|
|
|
|
|
return Ok((file_id, new_start..new_end));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
panic!("internal error: can't find span in parser state")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn report_parsing_error(
|
|
|
|
working_set: &ParserWorkingSet,
|
|
|
|
error: &ParseError,
|
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
|
|
|
let config = codespan_reporting::term::Config::default();
|
|
|
|
|
|
|
|
let diagnostic =
|
|
|
|
match error {
|
|
|
|
ParseError::Mismatch(missing, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Type mismatch during operation")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("expected {}", missing))])
|
|
|
|
}
|
|
|
|
ParseError::ExtraTokens(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Extra tokens in code")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("extra tokens")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::ExtraPositional(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Extra positional argument")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message("extra positional argument")])
|
|
|
|
}
|
|
|
|
ParseError::UnexpectedEof(s, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unexpected end of code")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("expected {}", s))])
|
|
|
|
}
|
|
|
|
ParseError::Unclosed(delim, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unclosed delimiter")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("unclosed {}", delim))])
|
|
|
|
}
|
|
|
|
ParseError::UnknownStatement(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unknown statement")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("unknown statement")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::MultipleRestParams(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Multiple rest params")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message("multiple rest params")])
|
|
|
|
}
|
|
|
|
ParseError::VariableNotFound(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Variable not found")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("variable not found")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::UnknownCommand(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unknown command")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("unknown command")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::UnknownFlag(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unknown flag")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("unknown flag")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::UnknownType(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unknown type")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("unknown type")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::MissingFlagParam(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Missing flag param")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message("flag missing parameter")])
|
|
|
|
}
|
|
|
|
ParseError::ShortFlagBatchCantTakeArg(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Batches of short flags can't take arguments")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message("short flag batches can't take args")])
|
|
|
|
}
|
|
|
|
ParseError::MissingPositional(name, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Missing required positional arg")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("missing {}", name))])
|
|
|
|
}
|
|
|
|
ParseError::MissingType(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Missing type")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("expected type")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ParseError::TypeMismatch(ty, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Type mismatch")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("expected {:?}", ty))])
|
|
|
|
}
|
|
|
|
ParseError::MissingRequiredFlag(name, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Missing required flag")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("missing required flag {}", name))])
|
|
|
|
}
|
|
|
|
ParseError::IncompleteMathExpression(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Incomplete math expresssion")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message("incomplete math expression")])
|
|
|
|
}
|
|
|
|
ParseError::UnknownState(name, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unknown state")
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("unknown state {}", name))])
|
|
|
|
}
|
|
|
|
ParseError::NonUtf8(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Non-UTF8 code")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("non-UTF8 code")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// println!("DIAG");
|
|
|
|
// println!("{:?}", diagnostic);
|
|
|
|
codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn report_shell_error(
|
|
|
|
working_set: &ParserWorkingSet,
|
|
|
|
error: &ShellError,
|
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
|
|
|
let config = codespan_reporting::term::Config::default();
|
|
|
|
|
|
|
|
let diagnostic = match error {
|
2021-08-10 08:31:34 +02:00
|
|
|
ShellError::OperatorMismatch {
|
|
|
|
op_span,
|
|
|
|
lhs_ty,
|
|
|
|
lhs_span,
|
|
|
|
rhs_ty,
|
|
|
|
rhs_span,
|
|
|
|
} => {
|
|
|
|
let (lhs_file_id, lhs_range) = convert_span_to_diag(working_set, lhs_span)?;
|
|
|
|
let (rhs_file_id, rhs_range) = convert_span_to_diag(working_set, rhs_span)?;
|
|
|
|
let (op_file_id, op_range) = convert_span_to_diag(working_set, op_span)?;
|
2021-08-10 07:08:10 +02:00
|
|
|
Diagnostic::error()
|
2021-08-10 08:31:34 +02:00
|
|
|
.with_message("Type mismatch during operation")
|
2021-08-10 07:55:25 +02:00
|
|
|
.with_labels(vec![
|
2021-08-10 08:31:34 +02:00
|
|
|
Label::primary(op_file_id, op_range).with_message("type mismatch for operator"),
|
|
|
|
Label::secondary(lhs_file_id, lhs_range).with_message(lhs_ty.to_string()),
|
|
|
|
Label::secondary(rhs_file_id, rhs_range).with_message(rhs_ty.to_string()),
|
2021-08-10 07:55:25 +02:00
|
|
|
])
|
2021-08-10 07:08:10 +02:00
|
|
|
}
|
|
|
|
ShellError::Unsupported(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Unsupported operation")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("unsupported operation")
|
|
|
|
])
|
|
|
|
}
|
|
|
|
ShellError::InternalError(s) => {
|
|
|
|
Diagnostic::error().with_message(format!("Internal error: {}", s))
|
|
|
|
}
|
|
|
|
ShellError::VariableNotFound(span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message("Variable not found")
|
|
|
|
.with_labels(vec![
|
|
|
|
Label::primary(diag_file_id, diag_range).with_message("variable not found")
|
|
|
|
])
|
|
|
|
}
|
2021-08-10 07:55:25 +02:00
|
|
|
ShellError::CantConvert(s, span) => {
|
|
|
|
let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?;
|
|
|
|
Diagnostic::error()
|
|
|
|
.with_message(format!("Can't convert to {}", s))
|
|
|
|
.with_labels(vec![Label::primary(diag_file_id, diag_range)
|
|
|
|
.with_message(format!("can't convert to {}", s))])
|
|
|
|
}
|
2021-08-10 07:08:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// println!("DIAG");
|
|
|
|
// println!("{:?}", diagnostic);
|
|
|
|
codespan_reporting::term::emit(&mut writer.lock(), &config, working_set, &diagnostic)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|