organic/src/main.rs

190 lines
5.6 KiB
Rust
Raw Normal View History

2023-08-22 17:50:41 -04:00
use std::ops::RangeBounds;
use nom::{bytes::complete::tag, Compare, InputTake, Slice};
2023-08-22 17:20:01 -04:00
2023-08-22 17:11:45 -04:00
fn main() {
2023-08-22 17:20:01 -04:00
let input = "this is my test input".to_owned();
2023-08-22 17:24:26 -04:00
let wrapped_input = WrappedInput::new(input.as_str());
2023-08-22 17:20:01 -04:00
2023-08-22 17:24:26 -04:00
let output = tag::<_, _, (_, nom::error::ErrorKind)>("this")(wrapped_input).unwrap();
2023-08-22 17:20:01 -04:00
println!("{:#?}", output);
}
2023-08-22 17:24:26 -04:00
#[derive(Debug)]
2023-08-22 17:20:01 -04:00
struct WrappedInput<'s> {
2023-08-22 17:50:41 -04:00
full_source: &'s str,
start: usize,
end: usize, //exclusive
preceding_line_break: Option<usize>,
2023-08-22 17:11:45 -04:00
}
2023-08-22 17:24:26 -04:00
impl<'s> WrappedInput<'s> {
2023-08-22 17:50:41 -04:00
/// Returns a wrapped string that keeps track of values we need for parsing org-mode.
///
/// Only call this on the full original string. Calling this on a substring can result in invalid values.
2023-08-22 17:24:26 -04:00
pub fn new(input: &'s str) -> Self {
2023-08-22 17:50:41 -04:00
WrappedInput {
full_source: input,
start: 0,
end: input.len(),
preceding_line_break: None,
}
}
2023-08-22 22:18:44 -04:00
/// Get the text since the line break preceding the start of this WrappedInput.
pub fn text_since_line_break(&self) -> &'s str {
let start = self.preceding_line_break.unwrap_or(0);
&self.full_source[start..self.start]
}
2023-08-22 17:50:41 -04:00
}
impl<'s> InputTake for WrappedInput<'s> {
fn take(&self, count: usize) -> Self {
self.slice(..count)
}
fn take_split(&self, count: usize) -> (Self, Self) {
(self.slice(count..), self.slice(..count))
}
}
impl<'s, O: Into<&'s str>> Compare<O> for WrappedInput<'s> {
fn compare(&self, t: O) -> nom::CompareResult {
(&self.full_source[self.start..self.end]).compare(t.into())
}
fn compare_no_case(&self, t: O) -> nom::CompareResult {
(&self.full_source[self.start..self.end]).compare_no_case(t.into())
}
}
impl<'s> From<&'s str> for WrappedInput<'s> {
fn from(value: &'s str) -> Self {
WrappedInput::new(value)
}
}
impl<'s> From<&WrappedInput<'s>> for &'s str {
fn from(value: &WrappedInput<'s>) -> Self {
&value.full_source[value.start..value.end]
}
}
impl<'s, R> Slice<R> for WrappedInput<'s>
where
R: RangeBounds<usize>,
{
fn slice(&self, range: R) -> Self {
let new_start = match range.start_bound() {
std::ops::Bound::Included(idx) => self.start + idx,
std::ops::Bound::Excluded(idx) => self.start + idx - 1,
std::ops::Bound::Unbounded => self.start,
};
let new_end = match range.end_bound() {
2023-08-22 21:38:50 -04:00
std::ops::Bound::Included(idx) => self.start + idx + 1,
std::ops::Bound::Excluded(idx) => self.start + idx,
std::ops::Bound::Unbounded => self.end,
2023-08-22 17:50:41 -04:00
};
2023-08-22 21:38:50 -04:00
if new_start < self.start {
panic!("Attempted to extend before the start of the WrappedInput.")
}
if new_end > self.end {
panic!("Attempted to extend past the end of the WrappedInput.")
}
2023-08-22 17:50:41 -04:00
2023-08-22 22:18:44 -04:00
let skipped_text = &self.full_source[self.start..new_start];
let last_line_feed = skipped_text
.rfind('\n')
.map(|idx| idx + self.preceding_line_break.unwrap_or(0) + 1);
2023-08-22 17:50:41 -04:00
// TODO: calculate updated values for WrappedInput
WrappedInput {
full_source: self.full_source,
start: new_start,
end: new_end,
2023-08-22 22:18:44 -04:00
preceding_line_break: last_line_feed,
2023-08-22 17:50:41 -04:00
}
}
}
impl<'s> std::fmt::Display for WrappedInput<'s> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Into::<&str>::into(self).fmt(f)
2023-08-22 17:24:26 -04:00
}
}
2023-08-22 21:38:50 -04:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn range() {
let input = WrappedInput::new("foo bar baz");
let output = input.slice(4..7);
assert_eq!(output.to_string(), "bar");
}
#[test]
fn range_to() {
let input = WrappedInput::new("foo bar baz");
let output = input.slice(..7);
assert_eq!(output.to_string(), "foo bar");
}
#[test]
fn range_from() {
let input = WrappedInput::new("foo bar baz");
let output = input.slice(4..);
assert_eq!(output.to_string(), "bar baz");
}
#[test]
fn full_range() {
let input = WrappedInput::new("foo bar baz");
let output = input.slice(..);
assert_eq!(output.to_string(), "foo bar baz");
}
#[test]
fn nested_range() {
let input = WrappedInput::new("lorem foo bar baz ipsum");
let first_cut = input.slice(6..17);
let output = first_cut.slice(4..7);
assert_eq!(first_cut.to_string(), "foo bar baz");
assert_eq!(output.to_string(), "bar");
}
#[test]
#[should_panic]
fn out_of_bounds() {
let input = WrappedInput::new("lorem foo bar baz ipsum");
input.slice(6..30);
}
#[test]
#[should_panic]
fn out_of_nested_bounds() {
let input = WrappedInput::new("lorem foo bar baz ipsum");
let first_cut = input.slice(6..17);
first_cut.slice(4..14);
}
2023-08-22 22:18:44 -04:00
#[test]
fn line_break() {
let input = WrappedInput::new("lorem\nfoo\nbar\nbaz\nipsum");
assert_eq!(input.slice(5..).preceding_line_break, None);
assert_eq!(input.slice(6..).preceding_line_break, Some(6));
assert_eq!(input.slice(6..).slice(10..).preceding_line_break, Some(14));
}
#[test]
fn text_since_line_break() {
let input = WrappedInput::new("lorem\nfoo\nbar\nbaz\nipsum");
assert_eq!(input.text_since_line_break(), "");
assert_eq!(input.slice(5..).text_since_line_break(), "lorem");
assert_eq!(input.slice(6..).text_since_line_break(), "");
assert_eq!(input.slice(6..).slice(10..).text_since_line_break(), "ba");
}
2023-08-22 21:38:50 -04:00
}