diff options
Diffstat (limited to 'src/parse')
| -rw-r--r-- | src/parse/mod.rs | 84 | ||||
| -rw-r--r-- | src/parse/span.rs | 86 |
2 files changed, 166 insertions, 4 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 102b334..97a6e4a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -3,6 +3,8 @@ use crate::{BString, PushAll, bstr}; #[cfg(test)] mod test; +mod span; + pub trait Stage: PartialEq { type Str: std::fmt::Debug + Clone + PartialEq; } @@ -908,6 +910,7 @@ impl Parse for ExpString { let mut last_delim = StringDelimiter::None; 'outer: loop { + let begin = b.loc(); let Some(delim) = StringDelimiter::try_begin(b) else { break; }; @@ -1034,6 +1037,14 @@ impl Parse for ExpString { add_char(p, x); } } + + if !delim.is_none() { + let end = b.loc_u32(); + b.highlights.push(Highlight { + span: begin.to(end), + kind: HighlightKind::String, + }); + } } if already_parsed { @@ -1285,6 +1296,18 @@ pub enum ParseMode { Completion, } +#[derive(Copy, Clone)] +pub enum HighlightKind { + Keyword(Keyword), + String, + None, +} + +pub struct Highlight { + pub span: span::Span, + pub kind: HighlightKind, +} + pub struct Cursor<'a> { buf: &'a [u8], mode: ParseMode, @@ -1293,6 +1316,13 @@ pub struct Cursor<'a> { spaced: bool, pub backtrace: bool, + + pub highlights: Vec<Highlight>, + + file: span::FileId, + + buf_start: u64, + buf_len: u32, } #[derive(Default)] @@ -1305,11 +1335,20 @@ struct SpaceStats { impl<'a> Cursor<'a> { pub fn new(buf: &'a [u8], mode: ParseMode) -> Self { + assert!( + buf.len() < u32::MAX as usize, + "cannot support larger parse buffers for now - what are you even doing." + ); + Self { buf, mode, spaced: false, backtrace: false, + highlights: Vec::new(), + file: span::FileId::new(), + buf_start: buf.as_ptr() as u64, + buf_len: buf.len() as u32, } } @@ -1377,6 +1416,18 @@ impl<'a> Cursor<'a> { } } + fn loc_u32(&self) -> u32 { + let now_loc = self.buf.as_ptr() as u64; + assert!(now_loc >= self.buf_start, "not the original buffer"); + let relative_loc = (now_loc - self.buf_start) as u32; + assert!(relative_loc <= self.buf_len, "not the original buffer"); + relative_loc + } + + fn loc(&self) -> span::SpanFrom { + self.file.from(self.loc_u32()) + } + fn spaces_stats(&mut self) -> SpaceStats { let mut stats = SpaceStats::default(); while self.has() && b" \t\n\r".contains(&self.buf[0]) { @@ -1418,7 +1469,8 @@ impl<'a> Cursor<'a> { } self.spaces(); - if self.buf.starts_with(bytes) { + let span = self.loc().with_len(bytes.len() as u32); + let result = if self.buf.starts_with(bytes) { if kw.requires_space() { if self.buf.len() > bytes.len() && self.buf[bytes.len()].is_ascii_whitespace() { self.buf = &self.buf[bytes.len() + 1..]; @@ -1439,11 +1491,20 @@ impl<'a> Cursor<'a> { } } else { Err(ParseError::ExpectedKeyword(kw)) + }; + + if result.is_ok() { + self.highlights.push(Highlight { + span, + kind: HighlightKind::Keyword(kw), + }) } + + result } } -#[derive(Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum Keyword { If, While, @@ -1546,8 +1607,8 @@ impl Parse for While { } } -impl Parse for Ast<PreExpansion> { - fn parse(b: &mut Cursor<'_>) -> Result<Self> { +impl Ast<PreExpansion> { + fn parse_inner(b: &mut Cursor<'_>) -> Result<Self> { b.spaces(); let orig_len = b.buf.len(); @@ -1586,6 +1647,21 @@ impl Parse for Ast<PreExpansion> { } } +impl Parse for Ast<PreExpansion> { + fn parse(b: &mut Cursor<'_>) -> Result<Self> { + let begin = b.loc(); + let result = Ast::parse_inner(b); + let span = begin.to(b.loc_u32()); + if result.is_ok() { + b.highlights.push(Highlight { + span, + kind: HighlightKind::None, + }); + } + result + } +} + impl Parse for Command<PreExpansion> { fn parse(b: &mut Cursor<'_>) -> Result<Self> { let path: ExpString = b.parse()?; diff --git a/src/parse/span.rs b/src/parse/span.rs new file mode 100644 index 0000000..340a078 --- /dev/null +++ b/src/parse/span.rs @@ -0,0 +1,86 @@ +use std::sync::atomic::AtomicU32; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FileId { + id: u32, +} + +impl FileId { + pub fn new() -> Self { + static GEN: AtomicU32 = AtomicU32::new(0); + Self { + id: GEN.fetch_add(1, std::sync::atomic::Ordering::SeqCst), + } + } + + pub fn from(self, start: u32) -> SpanFrom { + SpanFrom::new(self, start) + } + + pub fn span(self, start: u32, end: u32) -> Span { + Span::new(self, start, end) + } +} + +#[derive(Copy, Clone)] +pub struct SpanFrom { + pub file: FileId, + pub start: u32, +} + +impl SpanFrom { + pub fn new(file: FileId, start: u32) -> Self { + Self { file, start } + } + + pub fn to(self, end: u32) -> Span { + Span::new(self.file, self.start, end) + } + + pub fn with_len(self, len: u32) -> Span { + self.to(self.start + len) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Span { + pub file: FileId, + pub start: u32, + pub end: u32, +} + +/// manual implementation of PartialOrd to ensure shorter Spans are first +impl PartialOrd for Span { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + match self.file.partial_cmp(&other.file) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.start.partial_cmp(&other.start) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + other.end.partial_cmp(&self.end) + } +} + +/// manual implementation of Ord to ensure shorter Spans are first +impl Ord for Span { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match self.file.cmp(&other.file) { + core::cmp::Ordering::Equal => {} + ord => return ord, + } + match self.start.cmp(&other.start) { + core::cmp::Ordering::Equal => {} + ord => return ord, + } + other.end.cmp(&self.end) + } +} + +impl Span { + pub fn new(file: FileId, start: u32, end: u32) -> Self { + Self { file, start, end } + } +} |
