From c52f3527aa5248d69a234db4d258b56b36c4c33d Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sat, 30 Dec 2023 14:27:16 -0800 Subject: [PATCH] Introduce HasSpan trait (#20) --- src/blocks/block.rs | 11 +++- src/blocks/simple.rs | 21 +++++--- src/document/mod.rs | 13 ++--- src/has_span.rs | 9 ++++ src/lib.rs | 3 ++ .../asciidoc_lang/root/document_structure.rs | 18 +++++++ src/tests/blocks/block.rs | 52 ++++++++++++++++++- src/tests/blocks/simple.rs | 18 +++++++ src/tests/document/mod.rs | 18 +++++++ src/tests/fixtures/blocks/simple.rs | 8 ++- src/tests/fixtures/document/tdocument.rs | 1 + 11 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 src/has_span.rs diff --git a/src/blocks/block.rs b/src/blocks/block.rs index ce414bc..33174ee 100644 --- a/src/blocks/block.rs +++ b/src/blocks/block.rs @@ -1,7 +1,7 @@ use nom::IResult; use super::SimpleBlock; -use crate::{primitives::consume_empty_lines, Span}; +use crate::{primitives::consume_empty_lines, HasSpan, Span}; /// Block elements form the main structure of an AsciiDoc document, starting /// with the document itself. @@ -20,7 +20,6 @@ pub enum Block<'a> { } impl<'a> Block<'a> { - #[allow(dead_code)] /// Parse a block of any type and return a `Block` that describes it. /// /// Consumes any blank lines before and after the block. @@ -33,3 +32,11 @@ impl<'a> Block<'a> { Ok((rem, Self::Simple(simple_block))) } } + +impl<'a> HasSpan<'a> for Block<'a> { + fn span(&'a self) -> &'a Span<'a> { + match self { + Self::Simple(b) => b.span(), + } + } +} diff --git a/src/blocks/simple.rs b/src/blocks/simple.rs index 72e25fd..1d5f787 100644 --- a/src/blocks/simple.rs +++ b/src/blocks/simple.rs @@ -1,8 +1,8 @@ use nom::{multi::many1, IResult}; use crate::{ - primitives::{consume_empty_lines, non_empty_line}, - Span, + primitives::{consume_empty_lines, non_empty_line, trim_input_for_rem}, + HasSpan, Span, }; /// A block that's treated as contiguous lines of paragraph text (and subject to @@ -10,13 +10,22 @@ use crate::{ #[derive(Clone, Debug, Eq, PartialEq)] pub struct SimpleBlock<'a> { /// Lines that were found. + /// TO DO: Make private pub inlines: Vec>, + + source: Span<'a>, } impl<'a> SimpleBlock<'a> { - #[allow(dead_code)] // TEMPORARY - pub(crate) fn parse(i: Span<'a>) -> IResult { - let (i, inlines) = many1(non_empty_line)(i)?; - Ok((consume_empty_lines(i), Self { inlines })) + pub(crate) fn parse(source: Span<'a>) -> IResult { + let (rem, inlines) = many1(non_empty_line)(source)?; + let source = trim_input_for_rem(source, rem); + Ok((consume_empty_lines(rem), Self { inlines, source })) + } +} + +impl<'a> HasSpan<'a> for SimpleBlock<'a> { + fn span(&'a self) -> &'a Span<'a> { + &self.source } } diff --git a/src/document/mod.rs b/src/document/mod.rs index 3e13dbd..0e87543 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -4,7 +4,7 @@ use std::slice::Iter; use nom::IResult; -use crate::{blocks::Block, primitives::consume_empty_lines, Error, Span}; +use crate::{blocks::Block, primitives::consume_empty_lines, Error, HasSpan, Span}; /// A document represents the top-level block element in AsciiDoc. It consists /// of an optional document header and either a) one or more sections preceded @@ -38,17 +38,18 @@ impl<'a> Document<'a> { Ok(Self { source, blocks }) } - /// Return a [`Span`] describing the entire document as parsed. - pub fn span(&'a self) -> &'a Span<'a> { - &self.source - } - /// Return an iterator over the blocks in this document. pub fn blocks(&'a self) -> Iter<'a, Block<'a>> { self.blocks.iter() } } +impl<'a> HasSpan<'a> for Document<'a> { + fn span(&'a self) -> &'a Span<'a> { + &self.source + } +} + fn parse_blocks<'a>(mut i: Span<'a>) -> IResult>> { let mut blocks: Vec> = vec![]; i = consume_empty_lines(i); diff --git a/src/has_span.rs b/src/has_span.rs new file mode 100644 index 0000000..4aafedb --- /dev/null +++ b/src/has_span.rs @@ -0,0 +1,9 @@ +use crate::Span; + +/// Any syntactic element can describe its location +/// within the source material using this trait. +pub trait HasSpan<'a> { + /// Return a [`Span`] describing the syntactic element's + /// location within the source string/file. + fn span(&'a self) -> &'a Span<'a>; +} diff --git a/src/lib.rs b/src/lib.rs index 5044c00..9fe0792 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,9 @@ pub mod document; mod error; pub use error::{Error, ParseResult}; +mod has_span; +pub use has_span::HasSpan; + pub(crate) mod primitives; pub use primitives::Span; pub mod strings; diff --git a/src/tests/asciidoc_lang/root/document_structure.rs b/src/tests/asciidoc_lang/root/document_structure.rs index 81476f0..7c31d14 100644 --- a/src/tests/asciidoc_lang/root/document_structure.rs +++ b/src/tests/asciidoc_lang/root/document_structure.rs @@ -54,6 +54,12 @@ mod documents { col: 1, offset: 0, },], + source: TSpan { + data: "This is a basic AsciiDoc document.\n", + line: 1, + col: 1, + offset: 0, + } })], } ); @@ -97,6 +103,12 @@ mod documents { col: 1, offset: 0, },], + source: TSpan { + data: "This is a basic AsciiDoc document.\n", + line: 1, + col: 1, + offset: 0, + } }), TBlock::Simple(TSimpleBlock { inlines: vec![TSpan { @@ -105,6 +117,12 @@ mod documents { col: 1, offset: 36, },], + source: TSpan { + data: "This document contains two paragraphs.\n", + line: 3, + col: 1, + offset: 36, + } }) ], } diff --git a/src/tests/blocks/block.rs b/src/tests/blocks/block.rs index 4f39768..82ec1f7 100644 --- a/src/tests/blocks/block.rs +++ b/src/tests/blocks/block.rs @@ -11,7 +11,7 @@ mod simple { blocks::{TBlock, TSimpleBlock}, TSpan, }, - Span, + HasSpan, Span, }; #[test] @@ -66,9 +66,25 @@ mod simple { line: 1, col: 1, offset: 0, - }] + }], + source: TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + } }) ); + + assert_eq!( + block.span(), + TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + } + ); } #[test] @@ -102,8 +118,24 @@ mod simple { offset: 4, }, ], + source: TSpan { + data: "abc\ndef", + line: 1, + col: 1, + offset: 0, + } }) ); + + assert_eq!( + block.span(), + TSpan { + data: "abc\ndef", + line: 1, + col: 1, + offset: 0, + } + ); } #[test] @@ -129,7 +161,23 @@ mod simple { col: 1, offset: 0, }], + source: TSpan { + data: "abc\n", + line: 1, + col: 1, + offset: 0, + }, }) ); + + assert_eq!( + block.span(), + TSpan { + data: "abc\n", + line: 1, + col: 1, + offset: 0, + } + ); } } diff --git a/src/tests/blocks/simple.rs b/src/tests/blocks/simple.rs index f3a835c..6cb70be 100644 --- a/src/tests/blocks/simple.rs +++ b/src/tests/blocks/simple.rs @@ -51,6 +51,12 @@ fn single_line() { col: 1, offset: 0, }], + source: TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + } } ); } @@ -86,6 +92,12 @@ fn multiple_lines() { offset: 4, } ], + source: TSpan { + data: "abc\ndef", + line: 1, + col: 1, + offset: 0, + } } ); } @@ -113,6 +125,12 @@ fn consumes_blank_lines_after() { col: 1, offset: 0, }], + source: TSpan { + data: "abc\n", + line: 1, + col: 1, + offset: 0, + } } ); } diff --git a/src/tests/document/mod.rs b/src/tests/document/mod.rs index 5cb8e0c..92f10f2 100644 --- a/src/tests/document/mod.rs +++ b/src/tests/document/mod.rs @@ -59,6 +59,12 @@ fn one_simple_block() { col: 1, offset: 0, },], + source: TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + } })], } ); @@ -83,6 +89,12 @@ fn two_simple_blocks() { col: 1, offset: 0, },], + source: TSpan { + data: "abc\n", + line: 1, + col: 1, + offset: 0, + } }), TBlock::Simple(TSimpleBlock { inlines: vec![TSpan { @@ -91,6 +103,12 @@ fn two_simple_blocks() { col: 1, offset: 5, },], + source: TSpan { + data: "def", + line: 3, + col: 1, + offset: 5, + } }), ], } diff --git a/src/tests/fixtures/blocks/simple.rs b/src/tests/fixtures/blocks/simple.rs index 75235d2..4ff909c 100644 --- a/src/tests/fixtures/blocks/simple.rs +++ b/src/tests/fixtures/blocks/simple.rs @@ -1,10 +1,11 @@ use std::fmt; -use crate::{blocks::SimpleBlock, tests::fixtures::TSpan}; +use crate::{blocks::SimpleBlock, tests::fixtures::TSpan, HasSpan}; #[derive(Eq, PartialEq)] pub(crate) struct TSimpleBlock { pub inlines: Vec, + pub source: TSpan, } impl fmt::Debug for TSimpleBlock { @@ -14,6 +15,7 @@ impl fmt::Debug for TSimpleBlock { // differences. f.debug_struct("SimpleBlock") .field("inlines", &self.inlines) + .field("source", &self.source) .finish() } } @@ -35,6 +37,10 @@ fn tsimple_block_eq(tsimple_block: &TSimpleBlock, simple_block: &SimpleBlock) -> return false; } + if &tsimple_block.source != simple_block.span() { + return false; + } + for (tsb_line, sb_line) in tsimple_block .inlines .iter() diff --git a/src/tests/fixtures/document/tdocument.rs b/src/tests/fixtures/document/tdocument.rs index fdfc806..c585132 100644 --- a/src/tests/fixtures/document/tdocument.rs +++ b/src/tests/fixtures/document/tdocument.rs @@ -3,6 +3,7 @@ use std::{cmp::PartialEq, fmt}; use crate::{ document::Document, tests::fixtures::{blocks::TBlock, TSpan}, + HasSpan, }; // Approximate mock of Document type that we can use