Merge branch 'radio_link'
This commit is contained in:
commit
db0ea7394e
2
build.rs
2
build.rs
@ -79,8 +79,6 @@ fn is_expect_fail(name: &str) -> Option<&str> {
|
|||||||
"element_container_priority_greater_block_greater_block" => Some("Need to implement subscript."),
|
"element_container_priority_greater_block_greater_block" => Some("Need to implement subscript."),
|
||||||
"element_container_priority_section_greater_block" => Some("Need to implement subscript."),
|
"element_container_priority_section_greater_block" => Some("Need to implement subscript."),
|
||||||
"paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."),
|
"paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."),
|
||||||
"radio_link_before_and_after" => Some("Matching the contents of radio targets not yet implemented."),
|
|
||||||
"radio_link_simple" => Some("Matching the contents of radio targets not yet implemented."),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
alpha *bar* baz foo <<<*bar* baz>>> lorem ipsum *bar* baz dolar.
|
@ -18,6 +18,8 @@ use super::element::Element;
|
|||||||
use super::object::Object;
|
use super::object::Object;
|
||||||
use super::parser_with_context::parser_with_context;
|
use super::parser_with_context::parser_with_context;
|
||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
|
use super::token::AllTokensIterator;
|
||||||
|
use super::token::Token;
|
||||||
use super::util::exit_matcher_parser;
|
use super::util::exit_matcher_parser;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
use super::util::start_of_line;
|
use super::util::start_of_line;
|
||||||
@ -95,8 +97,35 @@ pub fn document(input: &str) -> Res<&str, Document> {
|
|||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||||
let document_context =
|
let document_context =
|
||||||
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
|
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
|
||||||
let zeroth_section_matcher = parser_with_context!(zeroth_section)(&document_context);
|
let (remaining, document) = _document(&document_context, input)?;
|
||||||
let heading_matcher = parser_with_context!(heading)(&document_context);
|
{
|
||||||
|
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
|
||||||
|
let all_radio_targets: Vec<&Vec<Object<'_>>> = document
|
||||||
|
.iter_tokens()
|
||||||
|
.filter_map(|tkn| match tkn {
|
||||||
|
Token::Object(obj) => Some(obj),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter_map(|obj| match obj {
|
||||||
|
Object::RadioTarget(rt) => Some(rt),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.map(|rt| &rt.children)
|
||||||
|
.collect();
|
||||||
|
if !all_radio_targets.is_empty() {
|
||||||
|
let document_context = document_context
|
||||||
|
.with_additional_node(ContextElement::RadioTarget(all_radio_targets));
|
||||||
|
let (remaining, document) = _document(&document_context, input)?;
|
||||||
|
return Ok((remaining, document));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((remaining, document))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Document<'s>> {
|
||||||
|
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
|
||||||
|
let heading_matcher = parser_with_context!(heading)(context);
|
||||||
let (remaining, _blank_lines) = many0(blank_line)(input)?;
|
let (remaining, _blank_lines) = many0(blank_line)(input)?;
|
||||||
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
|
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
|
||||||
let (remaining, children) = many0(heading_matcher)(remaining)?;
|
let (remaining, children) = many0(heading_matcher)(remaining)?;
|
||||||
@ -255,3 +284,9 @@ fn headline<'r, 's>(
|
|||||||
fn headline_end<'r, 's>(_context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
fn headline_end<'r, 's>(_context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
||||||
line_ending(input)
|
line_ending(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> Document<'s> {
|
||||||
|
pub fn iter_tokens<'r>(&'r self) -> impl Iterator<Item = Token<'r, 's>> {
|
||||||
|
AllTokensIterator::new(Token::Document(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@ use super::lesser_element::SrcBlock;
|
|||||||
use super::lesser_element::VerseBlock;
|
use super::lesser_element::VerseBlock;
|
||||||
use super::source::SetSource;
|
use super::source::SetSource;
|
||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
|
use super::token::Token;
|
||||||
use super::Drawer;
|
use super::Drawer;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -35,6 +35,7 @@ pub mod sexp;
|
|||||||
mod source;
|
mod source;
|
||||||
mod table;
|
mod table;
|
||||||
mod text_markup;
|
mod text_markup;
|
||||||
|
mod token;
|
||||||
mod util;
|
mod util;
|
||||||
pub use document::document;
|
pub use document::document;
|
||||||
pub use document::Document;
|
pub use document::Document;
|
||||||
|
@ -6,6 +6,7 @@ use nom::IResult;
|
|||||||
use super::list::List;
|
use super::list::List;
|
||||||
use super::list::Node;
|
use super::list::Node;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
|
use super::Object;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
@ -133,6 +134,13 @@ pub enum ContextElement<'r, 's> {
|
|||||||
|
|
||||||
/// Indicates if elements should consume the whitespace after them.
|
/// Indicates if elements should consume the whitespace after them.
|
||||||
ConsumeTrailingWhitespace(bool),
|
ConsumeTrailingWhitespace(bool),
|
||||||
|
|
||||||
|
/// The contents of a radio target.
|
||||||
|
///
|
||||||
|
/// If any are found, this will force a 2nd parse through the
|
||||||
|
/// org-mode document since text needs to be re-parsed to look for
|
||||||
|
/// radio links matching the contents of radio targets.
|
||||||
|
RadioTarget(Vec<&'r Vec<Object<'s>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExitMatcherNode<'r> {
|
pub struct ExitMatcherNode<'r> {
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
|
use nom::combinator::map;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
|
||||||
use super::object::PlainText;
|
use super::object::PlainText;
|
||||||
|
use super::radio_link::RematchObject;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
|
use super::Object;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::object_parser::any_object_except_plain_text;
|
use crate::parser::object_parser::any_object_except_plain_text;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
@ -33,12 +37,24 @@ fn plain_text_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
|
|||||||
recognize(parser_with_context!(any_object_except_plain_text)(context))(input)
|
recognize(parser_with_context!(any_object_except_plain_text)(context))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'x> RematchObject<'x> for PlainText<'x> {
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
fn rematch_object<'r, 's>(
|
||||||
|
&'x self,
|
||||||
|
_context: Context<'r, 's>,
|
||||||
|
input: &'s str,
|
||||||
|
) -> Res<&'s str, Object<'s>> {
|
||||||
|
map(tag(self.source), |s| {
|
||||||
|
Object::PlainText(PlainText { source: s })
|
||||||
|
})(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::parser::object::Object;
|
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ContextTree;
|
use crate::parser::parser_context::ContextTree;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
|
@ -6,6 +6,9 @@ use nom::combinator::verify;
|
|||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
|
||||||
use super::Context;
|
use super::Context;
|
||||||
|
use super::Object;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
use crate::parser::object_parser::minimal_set_object;
|
use crate::parser::object_parser::minimal_set_object;
|
||||||
@ -14,14 +17,66 @@ use crate::parser::parser_context::ExitMatcherNode;
|
|||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::not_yet_implemented;
|
|
||||||
use crate::parser::RadioLink;
|
use crate::parser::RadioLink;
|
||||||
use crate::parser::RadioTarget;
|
use crate::parser::RadioTarget;
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
pub fn radio_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, RadioLink<'s>> {
|
pub fn radio_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, RadioLink<'s>> {
|
||||||
not_yet_implemented()?;
|
let radio_targets = context
|
||||||
todo!();
|
.iter()
|
||||||
|
.filter_map(|context_element| match context_element.get_data() {
|
||||||
|
ContextElement::RadioTarget(targets) => Some(targets),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
for radio_target in radio_targets {
|
||||||
|
let rematched_target = rematch_target(context, radio_target, input);
|
||||||
|
if let Ok((remaining, rematched_target)) = rematched_target {
|
||||||
|
let (remaining, _) = space0(remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
return Ok((
|
||||||
|
remaining,
|
||||||
|
RadioLink {
|
||||||
|
source,
|
||||||
|
children: rematched_target,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"NoRadioLink",
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
pub fn rematch_target<'x, 'r, 's>(
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
target: &'x Vec<Object<'x>>,
|
||||||
|
input: &'s str,
|
||||||
|
) -> Res<&'s str, Vec<Object<'s>>> {
|
||||||
|
let mut remaining = input;
|
||||||
|
let mut new_matches = Vec::with_capacity(target.len());
|
||||||
|
for original_object in target {
|
||||||
|
match original_object {
|
||||||
|
// TODO: The rest of the minimal set of objects.
|
||||||
|
Object::Bold(bold) => {
|
||||||
|
let (new_remaining, new_match) = bold.rematch_object(context, remaining)?;
|
||||||
|
remaining = new_remaining;
|
||||||
|
new_matches.push(new_match);
|
||||||
|
}
|
||||||
|
Object::PlainText(plaintext) => {
|
||||||
|
let (new_remaining, new_match) = plaintext.rematch_object(context, remaining)?;
|
||||||
|
remaining = new_remaining;
|
||||||
|
new_matches.push(new_match);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"OnlyMinimalSetObjectsAllowed",
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok((remaining, new_matches))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
@ -54,3 +109,87 @@ pub fn radio_target<'r, 's>(
|
|||||||
fn radio_target_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
fn radio_target_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
||||||
alt((tag("<"), tag(">"), line_ending))(input)
|
alt((tag("<"), tag(">"), line_ending))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RematchObject<'x> {
|
||||||
|
fn rematch_object<'r, 's>(
|
||||||
|
&'x self,
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
input: &'s str,
|
||||||
|
) -> Res<&'s str, Object<'s>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::parser::element_parser::element;
|
||||||
|
use crate::parser::parser_context::ContextElement;
|
||||||
|
use crate::parser::parser_context::ContextTree;
|
||||||
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
|
use crate::parser::source::Source;
|
||||||
|
use crate::parser::Bold;
|
||||||
|
use crate::parser::PlainText;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_text_radio_target() {
|
||||||
|
let input = "foo bar baz";
|
||||||
|
let radio_target_match = vec![Object::PlainText(PlainText { source: "bar" })];
|
||||||
|
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||||
|
let document_context = initial_context
|
||||||
|
.with_additional_node(ContextElement::DocumentRoot(input))
|
||||||
|
.with_additional_node(ContextElement::RadioTarget(vec![&radio_target_match]));
|
||||||
|
let paragraph_matcher = parser_with_context!(element(true))(&document_context);
|
||||||
|
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph");
|
||||||
|
let first_paragraph = match first_paragraph {
|
||||||
|
crate::parser::Element::Paragraph(paragraph) => paragraph,
|
||||||
|
_ => panic!("Should be a paragraph!"),
|
||||||
|
};
|
||||||
|
assert_eq!(remaining, "");
|
||||||
|
assert_eq!(first_paragraph.get_source(), "foo bar baz");
|
||||||
|
assert_eq!(first_paragraph.children.len(), 3);
|
||||||
|
assert_eq!(
|
||||||
|
first_paragraph
|
||||||
|
.children
|
||||||
|
.get(1)
|
||||||
|
.expect("Len already asserted to be 3"),
|
||||||
|
&Object::RadioLink(RadioLink {
|
||||||
|
source: "bar ",
|
||||||
|
children: vec![Object::PlainText(PlainText { source: "bar" })]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bold_radio_target() {
|
||||||
|
let input = "foo *bar* baz";
|
||||||
|
let radio_target_match = vec![Object::Bold(Bold {
|
||||||
|
source: "*bar*",
|
||||||
|
children: vec![Object::PlainText(PlainText { source: "bar" })],
|
||||||
|
})];
|
||||||
|
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||||
|
let document_context = initial_context
|
||||||
|
.with_additional_node(ContextElement::DocumentRoot(input))
|
||||||
|
.with_additional_node(ContextElement::RadioTarget(vec![&radio_target_match]));
|
||||||
|
let paragraph_matcher = parser_with_context!(element(true))(&document_context);
|
||||||
|
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph");
|
||||||
|
let first_paragraph = match first_paragraph {
|
||||||
|
crate::parser::Element::Paragraph(paragraph) => paragraph,
|
||||||
|
_ => panic!("Should be a paragraph!"),
|
||||||
|
};
|
||||||
|
assert_eq!(remaining, "");
|
||||||
|
assert_eq!(first_paragraph.get_source(), "foo *bar* baz");
|
||||||
|
assert_eq!(first_paragraph.children.len(), 3);
|
||||||
|
assert_eq!(
|
||||||
|
first_paragraph
|
||||||
|
.children
|
||||||
|
.get(1)
|
||||||
|
.expect("Len already asserted to be 3"),
|
||||||
|
&Object::RadioLink(RadioLink {
|
||||||
|
source: "*bar* ",
|
||||||
|
children: vec![Object::Bold(Bold {
|
||||||
|
source: "*bar* ",
|
||||||
|
children: vec![Object::PlainText(PlainText { source: "bar" })]
|
||||||
|
})]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,8 +12,10 @@ use nom::combinator::recognize;
|
|||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::terminated;
|
use nom::sequence::terminated;
|
||||||
|
use nom::sequence::tuple;
|
||||||
use tracing::span;
|
use tracing::span;
|
||||||
|
|
||||||
|
use super::radio_link::RematchObject;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
@ -23,6 +25,7 @@ use crate::parser::object_parser::standard_set_object;
|
|||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
|
use crate::parser::radio_link::rematch_target;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::get_one_before;
|
use crate::parser::util::get_one_before;
|
||||||
@ -142,7 +145,6 @@ fn _text_markup_object<'r, 's, 'x>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Sometimes its plain text, not objects
|
|
||||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||||
Ok((remaining, children))
|
Ok((remaining, children))
|
||||||
@ -189,7 +191,6 @@ fn _text_markup_string<'r, 's, 'x>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Sometimes its plain text, not objects
|
|
||||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||||
Ok((remaining, contents))
|
Ok((remaining, contents))
|
||||||
@ -204,7 +205,7 @@ pub fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()>
|
|||||||
match preceding_character {
|
match preceding_character {
|
||||||
// If None, we are at the start of the file which is technically the beginning of a line.
|
// If None, we are at the start of the file which is technically the beginning of a line.
|
||||||
None | Some('\r') | Some('\n') | Some(' ') | Some('\t') | Some('-') | Some('(')
|
None | Some('\r') | Some('\n') | Some(' ') | Some('\t') | Some('-') | Some('(')
|
||||||
| Some('{') | Some('\'') | Some('"') => {}
|
| Some('{') | Some('\'') | Some('"') | Some('<') => {}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
// Not at start of line, cannot be a heading
|
// Not at start of line, cannot be a heading
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
@ -217,7 +218,7 @@ pub fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()>
|
|||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
pub fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> {
|
pub fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> {
|
||||||
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\"")), line_ending))(input)?;
|
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\">")), line_ending))(input)?;
|
||||||
Ok((remaining, ()))
|
Ok((remaining, ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,3 +243,53 @@ fn _text_markup_end<'r, 's, 'x>(
|
|||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((remaining, source))
|
Ok((remaining, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'x> RematchObject<'x> for Bold<'x> {
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
fn rematch_object<'r, 's>(
|
||||||
|
&'x self,
|
||||||
|
_context: Context<'r, 's>,
|
||||||
|
input: &'s str,
|
||||||
|
) -> Res<&'s str, Object<'s>> {
|
||||||
|
let (remaining, children) =
|
||||||
|
_rematch_text_markup_object(_context, input, "*", &self.children)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((remaining, Object::Bold(Bold { source, children })))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
fn _rematch_text_markup_object<'r, 's, 'x>(
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
input: &'s str,
|
||||||
|
marker_symbol: &'static str,
|
||||||
|
original_match_children: &'x Vec<Object<'x>>,
|
||||||
|
) -> Res<&'s str, Vec<Object<'s>>> {
|
||||||
|
let (remaining, _) = pre(context, input)?;
|
||||||
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
|
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
|
||||||
|
let text_markup_end_specialized = text_markup_end(open);
|
||||||
|
let parser_context =
|
||||||
|
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
|
class: ExitClass::Beta,
|
||||||
|
exit_matcher: &text_markup_end_specialized,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let (remaining, children) =
|
||||||
|
// TODO: This doesn't really check the exit matcher between each object. I think it may be possible to construct an org document that parses incorrectly with the current code.
|
||||||
|
rematch_target(&parser_context, original_match_children, remaining)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let span = span!(tracing::Level::DEBUG, "Checking parent exit.");
|
||||||
|
let _enter = span.enter();
|
||||||
|
if exit_matcher_parser(context, remaining).is_ok() {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"Parent exit matcher is triggering.",
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||||
|
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||||
|
Ok((remaining, children))
|
||||||
|
}
|
||||||
|
177
src/parser/token.rs
Normal file
177
src/parser/token.rs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use super::Document;
|
||||||
|
use super::Element;
|
||||||
|
use super::Heading;
|
||||||
|
use super::Object;
|
||||||
|
use super::PlainListItem;
|
||||||
|
use super::Section;
|
||||||
|
use super::TableCell;
|
||||||
|
use super::TableRow;
|
||||||
|
use crate::parser::DocumentElement;
|
||||||
|
|
||||||
|
pub enum Token<'r, 's> {
|
||||||
|
Document(&'r Document<'s>),
|
||||||
|
Heading(&'r Heading<'s>),
|
||||||
|
Section(&'r Section<'s>),
|
||||||
|
Object(&'r Object<'s>),
|
||||||
|
Element(&'r Element<'s>),
|
||||||
|
PlainListItem(&'r PlainListItem<'s>),
|
||||||
|
TableRow(&'r TableRow<'s>),
|
||||||
|
TableCell(&'r TableCell<'s>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, 's> Token<'r, 's> {
|
||||||
|
pub fn iter_tokens(&self) -> Box<dyn Iterator<Item = Token<'r, 's>> + '_> {
|
||||||
|
match self {
|
||||||
|
Token::Document(document) => Box::new(
|
||||||
|
document
|
||||||
|
.zeroth_section
|
||||||
|
.iter()
|
||||||
|
.map(Token::Section)
|
||||||
|
.chain(document.children.iter().map(Token::Heading)),
|
||||||
|
),
|
||||||
|
Token::Heading(heading) => Box::new(heading.title.iter().map(Token::Object).chain(
|
||||||
|
heading.children.iter().map(|de| match de {
|
||||||
|
DocumentElement::Heading(ref obj) => Token::Heading(obj),
|
||||||
|
DocumentElement::Section(ref obj) => Token::Section(obj),
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
Token::Section(section) => Box::new(section.children.iter().map(Token::Element)),
|
||||||
|
Token::Object(obj) => match obj {
|
||||||
|
Object::Bold(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::Italic(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::Underline(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::StrikeThrough(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::Code(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::Verbatim(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::PlainText(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::RegularLink(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::RadioLink(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::RadioTarget(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::PlainLink(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::AngleLink(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::OrgMacro(_) => Box::new(std::iter::empty()),
|
||||||
|
},
|
||||||
|
Token::Element(elem) => match elem {
|
||||||
|
Element::Paragraph(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Element::PlainList(inner) => {
|
||||||
|
Box::new(inner.children.iter().map(Token::PlainListItem))
|
||||||
|
}
|
||||||
|
Element::GreaterBlock(inner) => Box::new(inner.children.iter().map(Token::Element)),
|
||||||
|
Element::DynamicBlock(inner) => Box::new(inner.children.iter().map(Token::Element)),
|
||||||
|
Element::FootnoteDefinition(inner) => {
|
||||||
|
Box::new(inner.children.iter().map(Token::Element))
|
||||||
|
}
|
||||||
|
Element::Comment(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Drawer(inner) => Box::new(inner.children.iter().map(Token::Element)),
|
||||||
|
Element::PropertyDrawer(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Table(inner) => Box::new(inner.children.iter().map(Token::TableRow)),
|
||||||
|
Element::VerseBlock(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Element::CommentBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::ExampleBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::ExportBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::SrcBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Clock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::DiarySexp(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Planning(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::FixedWidthArea(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::HorizontalRule(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Keyword(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::LatexEnvironment(_) => Box::new(std::iter::empty()),
|
||||||
|
},
|
||||||
|
Token::PlainListItem(elem) => Box::new(elem.children.iter().map(Token::Element)),
|
||||||
|
Token::TableRow(elem) => Box::new(elem.children.iter().map(Token::TableCell)),
|
||||||
|
Token::TableCell(elem) => Box::new(elem.children.iter().map(Token::Object)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_tokens_no_order(&self) -> Box<dyn Iterator<Item = Token<'r, 's>> + '_> {
|
||||||
|
match self {
|
||||||
|
Token::Document(document) => Box::new(
|
||||||
|
document
|
||||||
|
.zeroth_section
|
||||||
|
.iter()
|
||||||
|
.map(Token::Section)
|
||||||
|
.chain(document.children.iter().map(Token::Heading)),
|
||||||
|
),
|
||||||
|
Token::Heading(heading) => Box::new(heading.title.iter().map(Token::Object).chain(
|
||||||
|
heading.children.iter().map(|de| match de {
|
||||||
|
DocumentElement::Heading(ref obj) => Token::Heading(obj),
|
||||||
|
DocumentElement::Section(ref obj) => Token::Section(obj),
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
Token::Section(section) => Box::new(section.children.iter().map(Token::Element)),
|
||||||
|
Token::Object(obj) => match obj {
|
||||||
|
Object::Bold(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::Italic(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::Underline(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::StrikeThrough(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::Code(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::Verbatim(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::PlainText(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::RegularLink(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::RadioLink(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::RadioTarget(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Object::PlainLink(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::AngleLink(_) => Box::new(std::iter::empty()),
|
||||||
|
Object::OrgMacro(_) => Box::new(std::iter::empty()),
|
||||||
|
},
|
||||||
|
Token::Element(elem) => match elem {
|
||||||
|
Element::Paragraph(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Element::PlainList(inner) => {
|
||||||
|
Box::new(inner.children.iter().map(Token::PlainListItem))
|
||||||
|
}
|
||||||
|
Element::GreaterBlock(inner) => Box::new(inner.children.iter().map(Token::Element)),
|
||||||
|
Element::DynamicBlock(inner) => Box::new(inner.children.iter().map(Token::Element)),
|
||||||
|
Element::FootnoteDefinition(inner) => {
|
||||||
|
Box::new(inner.children.iter().map(Token::Element))
|
||||||
|
}
|
||||||
|
Element::Comment(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Drawer(inner) => Box::new(inner.children.iter().map(Token::Element)),
|
||||||
|
Element::PropertyDrawer(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Table(inner) => Box::new(inner.children.iter().map(Token::TableRow)),
|
||||||
|
Element::VerseBlock(inner) => Box::new(inner.children.iter().map(Token::Object)),
|
||||||
|
Element::CommentBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::ExampleBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::ExportBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::SrcBlock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Clock(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::DiarySexp(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Planning(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::FixedWidthArea(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::HorizontalRule(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::Keyword(_) => Box::new(std::iter::empty()),
|
||||||
|
Element::LatexEnvironment(_) => Box::new(std::iter::empty()),
|
||||||
|
},
|
||||||
|
Token::PlainListItem(elem) => Box::new(elem.children.iter().map(Token::Element)),
|
||||||
|
Token::TableRow(elem) => Box::new(elem.children.iter().map(Token::TableCell)),
|
||||||
|
Token::TableCell(elem) => Box::new(elem.children.iter().map(Token::Object)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AllTokensIterator<'r, 's> {
|
||||||
|
queued_tokens: VecDeque<Token<'r, 's>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, 's> AllTokensIterator<'r, 's> {
|
||||||
|
pub fn new(tkn: Token<'r, 's>) -> Self {
|
||||||
|
let mut queued_tokens = VecDeque::new();
|
||||||
|
queued_tokens.push_back(tkn);
|
||||||
|
AllTokensIterator { queued_tokens }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, 's> Iterator for AllTokensIterator<'r, 's> {
|
||||||
|
type Item = Token<'r, 's>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let next_token = match self.queued_tokens.pop_front() {
|
||||||
|
Some(tkn) => tkn,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
self.queued_tokens.extend(next_token.iter_tokens());
|
||||||
|
Some(next_token)
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1 @@
|
|||||||
foo *bar /baz *lorem* ipsum/ dolar* alpha
|
foo <<<*bar* baz>>> lorem ipsum *bar* baz dolar.
|
||||||
|
|
||||||
foo *bar /baz _lorem_ ipsum/ dolar* alpha
|
|
||||||
|
Loading…
Reference in New Issue
Block a user