diff --git a/rusty_lr_buildscript/src/lib.rs b/rusty_lr_buildscript/src/lib.rs index cce7bd05..fe1c5aef 100644 --- a/rusty_lr_buildscript/src/lib.rs +++ b/rusty_lr_buildscript/src/lib.rs @@ -293,19 +293,17 @@ impl Builder { let diag = match e { ParseArgError::MacroLineParse { span, message } => { - let range = span.byte_range(); - Diagnostic::error() .with_message("Parse Failed") .with_labels(vec![ - Label::primary(file_id, range).with_message("Error here") + Label::primary(file_id, span).with_message("Error here") ]) .with_notes(vec![message]) } _ => { let message = e.short_message(); - let span = e.span().byte_range(); + let span = e.span(); Diagnostic::error().with_message(message).with_labels(vec![ Label::primary(file_id, span).with_message("occured here"), ]) @@ -321,13 +319,7 @@ impl Builder { }; for error in &grammar_args.error_recovered { - let range = if let Some((first, last)) = error.span.pair { - let first_range = first.byte_range(); - let last_range = last.byte_range(); - first_range.start..last_range.end - } else { - 0..1 // default range if span is not defined - }; + let range = error.span.to_range(); let diag = Diagnostic::error() .with_message("Syntax error in grammar") .with_labels(vec![ diff --git a/rusty_lr_parser/Cargo.toml b/rusty_lr_parser/Cargo.toml index ed32aef8..7645e2b8 100644 --- a/rusty_lr_parser/Cargo.toml +++ b/rusty_lr_parser/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["parser", "bison", "lr", "glr", "compiler"] categories = ["parsing"] [dependencies] -proc-macro2 = "1.0.86" +proc-macro2 = { version = "1.0.86", features = ["span-locations"] } quote = "1.0" rusty_lr_core = { version = "3.40.0", path = "../rusty_lr_core", features = [ "builder", diff --git a/rusty_lr_parser/src/error.rs b/rusty_lr_parser/src/error.rs index b8b7ab51..e741dc1e 100644 --- a/rusty_lr_parser/src/error.rs +++ b/rusty_lr_parser/src/error.rs @@ -6,13 +6,17 @@ use proc_macro2::TokenStream; use quote::quote_spanned; use crate::parser::args::IdentOrLiteral; +use crate::parser::span_pair::byte_range_to_span; /// failed to feed() the token #[non_exhaustive] #[derive(Debug)] pub enum ParseArgError { - /// feed() failed - MacroLineParse { span: Span, message: String }, + /// feed() failed; `span` is the byte range `[start, end)` in the source + MacroLineParse { + span: std::ops::Range, + message: String, + }, } #[non_exhaustive] @@ -195,17 +199,18 @@ impl ArgError { #[allow(unused)] impl ParseArgError { pub fn to_compile_error(&self) -> TokenStream { - let span = self.span(); let message = self.short_message(); + let span = byte_range_to_span(self.span()); quote_spanned! { span=> compile_error!(#message); } } - pub fn span(&self) -> Span { + /// Returns the byte range `[start, end)` of the error location in the source. + pub fn span(&self) -> std::ops::Range { match self { - ParseArgError::MacroLineParse { span, message } => *span, + ParseArgError::MacroLineParse { span, message } => span.clone(), } } diff --git a/rusty_lr_parser/src/grammar.rs b/rusty_lr_parser/src/grammar.rs index 8b602ced..20ba62e3 100644 --- a/rusty_lr_parser/src/grammar.rs +++ b/rusty_lr_parser/src/grammar.rs @@ -185,7 +185,7 @@ impl Grammar { Ok(_) => {} Err(err) => { let message = err.to_string(); - let span = err.location().unwrap().pair.unwrap().0; + let span = err.location().map(|loc| loc.to_range()).unwrap_or(0..0); return Err(ParseArgError::MacroLineParse { span, message }); } } @@ -193,8 +193,7 @@ impl Grammar { Ok(_) => {} Err(err) => { let message = err.to_string(); - let span = Span::call_site(); - return Err(ParseArgError::MacroLineParse { span, message }); + return Err(ParseArgError::MacroLineParse { span: 0..0, message }); } } diff --git a/rusty_lr_parser/src/parser/args.rs b/rusty_lr_parser/src/parser/args.rs index c16327a7..db1e491a 100644 --- a/rusty_lr_parser/src/parser/args.rs +++ b/rusty_lr_parser/src/parser/args.rs @@ -371,8 +371,8 @@ impl PatternArgs { } } PatternArgs::Sep(_, _, _, span) => { - let (first, last) = span.pair.unwrap(); - Err(ParseError::OnlyTerminalSet(first, last)) + let s = span.span(); + Err(ParseError::OnlyTerminalSet(s, s)) } } } @@ -400,7 +400,10 @@ impl PatternArgs { PatternArgs::Minus(base, terminal_set) => { (base.span_pair().0, terminal_set.span_pair().1) } - PatternArgs::Sep(_, _, _, span) => span.pair.unwrap(), + PatternArgs::Sep(_, _, _, span) => { + let s = span.span(); + (s, s) + } } } diff --git a/rusty_lr_parser/src/parser/span_pair.rs b/rusty_lr_parser/src/parser/span_pair.rs index fdaa59b8..af573b68 100644 --- a/rusty_lr_parser/src/parser/span_pair.rs +++ b/rusty_lr_parser/src/parser/span_pair.rs @@ -1,48 +1,63 @@ use proc_macro2::Span; +/// Converts a byte range `[start, end)` into a `proc_macro2::Span`. +/// Currently, byte offsets cannot be mapped back precisely, so this returns `Span::call_site()`. +pub fn byte_range_to_span(_range: std::ops::Range) -> Span { + Span::call_site() +} + /// type for %location for each token -/// since `Span::join()` is only for nightly, -/// we collect the first and last span pair of the token in the parsing tree. +/// stores byte offset range [start, end) in the source file. #[derive(Clone, Debug, Copy)] pub struct SpanPair { - /// `None` if this is a zero-length span - pub pair: Option<(Span, Span)>, + /// byte range `[start, end)` of the token span. + /// zero-length spans are represented with equal values `(pos, pos)`. + pub pair: (usize, usize), } impl Default for SpanPair { fn default() -> Self { - SpanPair { pair: None } + SpanPair { pair: (0, 0) } } } impl SpanPair { pub fn new_single(span: Span) -> Self { + let range = span.byte_range(); SpanPair { - pair: Some((span, span)), + pair: (range.start, range.end), } } + /// Returns the byte range `[start, end)` of this span. + pub fn to_range(&self) -> std::ops::Range { + let (s, e) = self.pair; + s..e + } + /// Returns a `proc_macro2::Span` for use in proc-macro error reporting. + /// Since byte offsets cannot be converted back to `Span`, this falls back to `byte_range_to_span`. pub fn span(&self) -> Span { - self.pair - .as_ref() - .map_or(Span::call_site(), |(first, last)| { - if let Some(joined) = first.join(*last) { - joined - } else { - *first - } - }) + byte_range_to_span(self.to_range()) } } impl rusty_lr_core::Location for SpanPair { - fn new<'a>(stack: impl Iterator + Clone, len: usize) -> Self + fn new<'a>(mut stack: impl Iterator + Clone, len: usize) -> Self where Self: 'a, { - let mut take = stack.take(len).filter_map(|x| x.pair); - let pair = if let Some(last) = take.next() { - let first = take.last().unwrap_or(last); - - Some((first.0, last.1)) + if len == 0 { + // zero-length: point to position after the most recent token + if let Some(after_pos) = stack.next() { + let (_, e) = after_pos.pair; + return SpanPair { pair: (e, e) }; + } + return SpanPair::default(); + } + // The iterator yields items most-recent-first, so the first item is the end span + // and the last item taken is the start span. + let mut take = stack.take(len).map(|x| x.pair); + let pair = if let Some(end_span) = take.next() { + let start_span = take.last().unwrap_or(end_span); + (start_span.0, end_span.1) } else { - None + (0, 0) }; SpanPair { pair } }