diff --git a/src/protocol/inputsource.rs b/src/protocol/inputsource.rs index f16da444b81e6f53a397487ec0ce14a34a11d235..858fdf25cd964674da1e9b859a5a6216de1e2aba 100644 --- a/src/protocol/inputsource.rs +++ b/src/protocol/inputsource.rs @@ -7,8 +7,8 @@ use backtrace::Backtrace; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct InputSource { - filename: String, - pub input: Vec, + pub(crate) filename: String, + pub(crate) input: Vec, line: usize, column: usize, offset: usize, @@ -87,9 +87,9 @@ impl InputSource { self.column = pos.column; self.offset = pos.offset; } - pub fn error(&self, message: S) -> ParseError { - self.pos().parse_error(message) - } + // pub fn error(&self, message: S) -> ParseError { + // self.pos().parse_error(message) + // } pub fn is_eof(&self) -> bool { self.next() == None } @@ -147,7 +147,7 @@ impl fmt::Display for InputSource { } } -#[derive(Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] pub struct InputPosition { line: usize, column: usize, @@ -167,14 +167,20 @@ impl InputPosition { } &source.input[start..end] } - fn parse_error(&self, message: S) -> ParseError { - ParseError { position: *self, message: message.to_string(), backtrace: Backtrace::new() } - } + // fn parse_error(&self, message: S) -> ParseError { + // ParseError { position: *self, message: message.to_string(), backtrace: Backtrace::new() } + // } fn eval_error(&self, message: S) -> EvalError { EvalError { position: *self, message: message.to_string(), backtrace: Backtrace::new() } } } +impl Default for InputPosition { + fn default() -> Self { + Self{ line: 1, column: 1, offset: 0 } + } +} + impl fmt::Display for InputPosition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.line, self.column) @@ -188,78 +194,132 @@ pub trait SyntaxElement { } } -#[derive(Debug, Clone)] -pub struct ParseError { +#[derive(Debug)] +pub enum ParseErrorType { + Info, + Error +} + +#[derive(Debug)] +pub struct ParseErrorStatement { + error_type: ParseErrorType, position: InputPosition, + filename: String, + context: String, message: String, - backtrace: Backtrace, } -impl ParseError { - pub fn new(position: InputPosition, message: S) -> ParseError { - ParseError { position, message: message.to_string(), backtrace: Backtrace::new() } +impl ParseErrorStatement { + fn from_source(error_type: ParseErrorType, source: &InputSource, position: InputPosition, msg: &str) -> Self { + // Seek line start and end + println!("DEBUG[1]:\nPos {}, msg: {},\nDEBUG[2]: In source:\n{}", + position, msg, String::from_utf8_lossy(&source.input)); + debug_assert!(position.column < position.offset); + let line_start = position.offset - (position.column - 1); + let mut line_end = position.offset; + while line_end < source.input.len() && source.input[line_end] != b'\n' { + line_end += 1; + } + + // Compensate for '\r\n' + if line_end > line_start && source.input[line_end - 1] == b'\r' { + line_end -= 1; + } + + Self{ + error_type, + position, + filename: source.filename.clone(), + context: String::from_utf8_lossy(&source.input[line_start..line_end]).to_string(), + message: msg.to_string() + } } - // Diagnostic methods - pub fn write(&self, source: &InputSource, writer: &mut A) -> io::Result<()> { - if !source.filename.is_empty() { - writeln!( - writer, - "Parse error at {}:{}: {}", - source.filename, self.position, self.message - )?; +} + +impl fmt::Display for ParseErrorStatement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Write message + match self.error_type { + ParseErrorType::Info => write!(f, " INFO: ")?, + ParseErrorType::Error => write!(f, "ERROR: ")?, + } + writeln!(f, "{}", &self.message); + + // Write originating file/line/column + if self.filename.is_empty() { + writeln!(f, " +- at {}:{}", self.position.line, self.position.column)?; } else { - writeln!(writer, "Parse error at {}: {}", self.position, self.message)?; + writeln!(f, " +- at {}:{}:{}", self.filename, self.position.line, self.position.column)?; } - let line = self.position.context(source); - writeln!(writer, "{}", String::from_utf8_lossy(line))?; - let mut arrow: Vec = Vec::new(); - for pos in 1..self.position.column { - let c = line[pos - 1]; - if c == b'\t' { - arrow.push(b'\t') + + // Write source context + writeln!(f, " | ")?; + writeln!(f, " | {}", self.context)?; + + // Write underline indicating where the error ocurred + debug_assert!(self.position.column <= self.context.chars().count()); + let mut arrow = String::with_capacity(self.context.len() + 3); + arrow.push_str(" | "); + let mut char_col = 1; + for char in self.context.chars() { + if char_col == self.position.column { break; } + if char == '\t' { + arrow.push('\t'); } else { - arrow.push(b' ') + arrow.push(' '); } + + char_col += 1; } - arrow.push(b'^'); - writeln!(writer, "{}", String::from_utf8_lossy(&arrow)) - } - pub fn print(&self, source: &InputSource) { - self.write(source, &mut std::io::stdout()).unwrap() - } - pub fn display<'a>(&'a self, source: &'a InputSource) -> DisplayParseError<'a> { - DisplayParseError::new(self, source) - } -} + arrow.push('^'); + writeln!(f, "{}", arrow)?; -impl From for io::Error { - fn from(_: ParseError) -> io::Error { - io::Error::new(io::ErrorKind::InvalidInput, "parse error") + Ok(()) } } -#[derive(Clone, Copy)] -pub struct DisplayParseError<'a> { - error: &'a ParseError, - source: &'a InputSource, +#[derive(Debug)] +pub struct ParseError2 { + statements: Vec } -impl DisplayParseError<'_> { - fn new<'a>(error: &'a ParseError, source: &'a InputSource) -> DisplayParseError<'a> { - DisplayParseError { error, source } +impl fmt::Display for ParseError2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.statements.is_empty() { + return Ok(()) + } + + self.statements[0].fmt(f)?; + for statement in self.statements.iter().skip(1) { + writeln!(f)?; + statement.fmt(f)?; + } + + Ok(()) } } -impl fmt::Display for DisplayParseError<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut vec: Vec = Vec::new(); - match self.error.write(self.source, &mut vec) { - Err(_) => { - return fmt::Result::Err(fmt::Error); - } - Ok(_) => {} - } - write!(f, "{}", String::from_utf8_lossy(&vec)) +impl ParseError2 { + pub fn empty() -> Self { + Self{ statements: Vec::new() } + } + + pub fn new_error(source: &InputSource, position: InputPosition, msg: &str) -> Self { + Self{ statements: vec!(ParseErrorStatement::from_source(ParseErrorType::Error, source, position, msg))} + } + + pub fn with_prefixed(mut self, error_type: ParseErrorType, source: &InputSource, position: InputPosition, msg: &str) -> Self { + self.statements.insert(0, ParseErrorStatement::from_source(error_type, source, position, msg)); + self + } + + pub fn with_postfixed(mut self, error_type: ParseErrorType, source: &InputSource, position: InputPosition, msg: &str) -> Self { + self.statements.push(ParseErrorStatement::from_source(error_type, source, position, msg)); + self + } + + pub fn with_postfixed_info(self, source: &InputSource, position: InputPosition, msg: &str) -> Self { + self.with_postfixed(ParseErrorType::Info, source, position, msg) } }