Skip to content

Commit

Permalink
Use nom-span for input types (#9)
Browse files Browse the repository at this point in the history
(Thank you, @JulesGuesnon!)
  • Loading branch information
scouten authored Dec 18, 2023
1 parent fe610c1 commit 97a9038
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 83 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ rust-version = "1.72.0"

[dependencies]
nom = "7.1"
nom-span = "0.1.1"
thiserror = "1.0.50"
10 changes: 3 additions & 7 deletions src/blocks/simple.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
use nom::{multi::many1, IResult};

use crate::primitives::non_empty_line;
use crate::{primitives::non_empty_line, Span};

/// A block that's treated as contiguous lines of paragraph text (and subject to
/// normal substitutions) (e.g., a paragraph block).
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SimpleBlock<'a> {
/// Lines that were found.
pub inlines: Vec<&'a str>,
pub inlines: Vec<Span<'a>>,
}

impl<'a> SimpleBlock<'a> {
/// Parse a byte-slice as a simple AsciiDoc block.
///
/// Returns a tuple of the remaining input and the simple block.
#[allow(dead_code)] // TEMPORARY
pub fn from_str(i: &'a str) -> IResult<&str, Self> {
pub(crate) fn parse(i: Span<'a>) -> IResult<Span, Self> {
let (i, inlines) = many1(non_empty_line)(i)?;

Ok((i, Self { inlines }))
}
}
7 changes: 4 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use nom::{
error::{ErrorKind, FromExternalError, ParseError},
IResult,
};
use nom_span::Spanned;

/// The error type for AsciiDoc parsing operations.
#[non_exhaustive]
Expand All @@ -16,12 +17,12 @@ pub enum Error {
NomError(ErrorKind),
}

impl<'a> ParseError<&'a str> for Error {
fn from_error_kind(_input: &'a str, kind: ErrorKind) -> Self {
impl<'a> ParseError<Spanned<&'a str>> for Error {
fn from_error_kind(_input: Spanned<&'a str>, kind: ErrorKind) -> Self {
Error::NomError(kind)
}

fn append(_input: &'a str, kind: ErrorKind, _other: Self) -> Self {
fn append(_input: Spanned<&'a str>, kind: ErrorKind, _other: Self) -> Self {
Error::NomError(kind)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod error;
pub use error::{Error, ParseResult};

pub(crate) mod primitives;
pub use primitives::Span;
pub mod strings;

#[cfg(test)]
Expand Down
32 changes: 16 additions & 16 deletions src/primitives/line.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use nom::{
bytes::complete::{take_till, take_till1},
IResult,
error::{Error, ErrorKind},
Err, IResult, Slice,
};

use crate::Span;

/// Return a single line from the source.
///
/// A line is terminated by end-of-input or a single `\n` character
/// or a single `\r\n` sequence. The end of line sequence is consumed
/// but not included in the returned line.
#[allow(dead_code)] // TEMPORARY
pub(crate) fn line(input: &str) -> IResult<&str, &str> {
pub(crate) fn line(input: Span<'_>) -> IResult<Span, Span> {
take_till(|c| c == '\n')(input)
.map(|ri| trim_rem_start_matches(ri, '\n'))
.map(|ri| trim_rem_end_matches(ri, '\r'))
Expand All @@ -23,7 +26,7 @@ pub(crate) fn line(input: &str) -> IResult<&str, &str> {
///
/// All trailing spaces are removed from the line.
#[allow(dead_code)] // TEMPORARY
pub(crate) fn normalized_line(input: &str) -> IResult<&str, &str> {
pub(crate) fn normalized_line(input: Span<'_>) -> IResult<Span, Span> {
take_till(|c| c == '\n')(input)
.map(|ri| trim_rem_start_matches(ri, '\n'))
.map(|ri| trim_rem_end_matches(ri, '\r'))
Expand All @@ -40,13 +43,7 @@ pub(crate) fn normalized_line(input: &str) -> IResult<&str, &str> {
///
/// Returns an error if the line becomes empty after trailing spaces have been
/// removed.
#[allow(dead_code)] // TEMPORARY
pub(crate) fn non_empty_line(input: &str) -> IResult<&str, &str> {
use nom::{
error::{Error, ErrorKind},
Err,
};

pub(crate) fn non_empty_line(input: Span<'_>) -> IResult<Span, Span> {
take_till1(|c| c == '\n')(input)
.map(|ri| trim_rem_start_matches(ri, '\n'))
.map(|ri| trim_rem_end_matches(ri, '\r'))
Expand All @@ -60,25 +57,28 @@ pub(crate) fn non_empty_line(input: &str) -> IResult<&str, &str> {
})
}

#[allow(dead_code)] // TEMPORARY
fn trim_rem_start_matches<'a>(rem_inp: (&'a str, &'a str), c: char) -> (&'a str, &'a str) {
fn trim_rem_start_matches<'a>(rem_inp: (Span<'a>, Span<'a>), c: char) -> (Span<'a>, Span<'a>) {
if let Some(rem) = rem_inp.0.strip_prefix(c) {
let prefix_len = rem_inp.0.len() - rem.len();
let rem = rem_inp.0.slice(prefix_len..);
(rem, rem_inp.1)
} else {
rem_inp
}
}

#[allow(dead_code)] // TEMPORARY
fn trim_rem_end_matches<'a>(rem_inp: (&'a str, &'a str), c: char) -> (&'a str, &'a str) {
fn trim_rem_end_matches<'a>(rem_inp: (Span<'a>, Span<'a>), c: char) -> (Span<'a>, Span<'a>) {
if let Some(inp) = rem_inp.1.strip_suffix(c) {
let inp = rem_inp.1.slice(0..inp.len());
(rem_inp.0, inp)
} else {
rem_inp
}
}

#[allow(dead_code)] // TEMPORARY
fn trim_trailing_spaces<'a>(rem_inp: (&'a str, &'a str)) -> (&'a str, &'a str) {
(rem_inp.0, rem_inp.1.trim_end_matches(' '))
fn trim_trailing_spaces<'a>(rem_inp: (Span<'a>, Span<'a>)) -> (Span<'a>, Span<'a>) {
let inp = rem_inp.1.trim_end_matches(' ');
let inp = rem_inp.1.slice(0..inp.len());
(rem_inp.0, inp)
}
13 changes: 10 additions & 3 deletions src/primitives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
//! Contains various primitive parsing routines.
//! Not part of the public API surface.

mod line;
#[allow(unused_imports)]
pub(crate) use line::{line, non_empty_line, normalized_line};

/// Represents a subset of the overall input stream.
///
/// Annotated with 1-based line and column numbers relative to the
/// beginning of the overall input stream.
///
/// Called `Span` because its `data` member can be consumed
/// to yield another `Span` with annotations for the end of the
/// syntactic element in question.
pub type Span<'a> = nom_span::Spanned<&'a str>;
41 changes: 27 additions & 14 deletions src/tests/blocks/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,56 @@ use nom::{
Err,
};

use crate::blocks::simple::SimpleBlock;
use crate::{blocks::simple::SimpleBlock, Span};

#[test]
fn empty_source() {
let expected_err: Err<Error<&str>> = Err::Error(Error::new("", ErrorKind::TakeTill1));
let expected_err = Err::Error(Error::new(Span::new("", true), ErrorKind::TakeTill1));

let actual_err = SimpleBlock::from_str("").unwrap_err();
let actual_err = SimpleBlock::parse(Span::new("", true)).unwrap_err();

assert_eq!(expected_err, actual_err);
}

#[test]
fn only_spaces() {
let expected_err: Err<Error<&str>> = Err::Error(Error::new(" ", ErrorKind::TakeTill1));
let expected_err = Err::Error(Error::new(Span::new(" ", true), ErrorKind::TakeTill1));

let actual_err = SimpleBlock::from_str(" ").unwrap_err();
let actual_err = SimpleBlock::parse(Span::new(" ", true)).unwrap_err();

assert_eq!(expected_err, actual_err);
}

#[test]
fn single_line() {
let expected = SimpleBlock {
inlines: vec!["abc"],
inlines: vec![Span::new("abc", true)],
};

assert_eq!(SimpleBlock::from_str("abc"), Ok(("", expected)));
let (rem, block) = SimpleBlock::parse(Span::new("abc", true)).unwrap();

assert_eq!(rem.line(), 1);
assert_eq!(rem.col(), 4);
assert_eq!(*rem.data(), "");

assert_eq!(block, expected);
}

#[test]
fn multiple_lines() {
let expected = SimpleBlock {
inlines: vec!["abc", "def"],
};
let (rem, block) = SimpleBlock::parse(Span::new("abc\ndef", true)).unwrap();

assert_eq!(rem.line(), 2);
assert_eq!(rem.col(), 4);
assert_eq!(*rem.data(), "");

assert_eq!(block.inlines.len(), 2);

assert_eq!(block.inlines[0].line(), 1);
assert_eq!(block.inlines[0].col(), 1);
assert_eq!(*block.inlines[0].data(), "abc");

assert_eq!(
SimpleBlock::from_str("abc\ndef\n\nghi"),
Ok(("\nghi", expected))
);
assert_eq!(block.inlines[1].line(), 2);
assert_eq!(block.inlines[1].col(), 1);
assert_eq!(*block.inlines[1].data(), "def");
}
Loading

0 comments on commit 97a9038

Please sign in to comment.