From fcf1ba4ea6be01734ccac345f039ea9e106855ea Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 16:43:23 -0400 Subject: [PATCH 01/14] Add an example of nested properties adding to previous properties. --- org_mode_samples/property_drawer/add_in_child.org | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 org_mode_samples/property_drawer/add_in_child.org diff --git a/org_mode_samples/property_drawer/add_in_child.org b/org_mode_samples/property_drawer/add_in_child.org new file mode 100644 index 0000000..aa3c11f --- /dev/null +++ b/org_mode_samples/property_drawer/add_in_child.org @@ -0,0 +1,9 @@ +* foo +** bar +:PROPERTIES: +:lorem: ipsum +:END: +*** baz +:PROPERTIES: +:lorem+: dolar +:END: From a267f23e0f6d89140d10ac710ec48a661b5998c7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 16:51:00 -0400 Subject: [PATCH 02/14] Initial code structure for property drawers. --- src/compare/diff.rs | 33 ++++++++++++++++++++++++++++++++- src/parser/element.rs | 9 +++++++++ src/parser/greater_element.rs | 7 +++++++ src/parser/mod.rs | 2 ++ src/parser/property_drawer.rs | 11 +++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/parser/property_drawer.rs diff --git a/src/compare/diff.rs b/src/compare/diff.rs index a1480aa..4920fb3 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -1,7 +1,6 @@ use super::sexp::Token; use super::util::assert_bounds; use super::util::assert_name; -use crate::compare::util::get_offsets; use crate::parser::Comment; use crate::parser::Document; use crate::parser::DocumentElement; @@ -13,6 +12,7 @@ use crate::parser::Heading; use crate::parser::Paragraph; use crate::parser::PlainList; use crate::parser::PlainListItem; +use crate::parser::PropertyDrawer; use crate::parser::Section; use crate::DynamicBlock; @@ -197,6 +197,7 @@ fn compare_element<'s>( Element::FootnoteDefinition(obj) => compare_footnote_definition(source, emacs, obj), Element::Comment(obj) => compare_comment(source, emacs, obj), Element::Drawer(obj) => compare_drawer(source, emacs, obj), + Element::PropertyDrawer(obj) => compare_property_drawer(source, emacs, obj), } } @@ -428,3 +429,33 @@ fn compare_drawer<'s>( children: child_status, }) } + +fn compare_property_drawer<'s>( + source: &'s str, + emacs: &'s Token<'s>, + rust: &'s PropertyDrawer<'s>, +) -> Result> { + let children = emacs.as_list()?; + let mut child_status = Vec::new(); + let mut this_status = DiffStatus::Good; + let emacs_name = "property-drawer"; + if assert_name(emacs, emacs_name).is_err() { + this_status = DiffStatus::Bad; + } + + if assert_bounds(source, emacs, rust).is_err() { + this_status = DiffStatus::Bad; + } + + for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { + // TODO: What are node properties and are they the only legal child of property drawers? + // child_status.push(compare_element(source, emacs_child, rust_child)?); + } + + Ok(DiffResult { + status: this_status, + name: emacs_name.to_owned(), + message: None, + children: child_status, + }) +} diff --git a/src/parser/element.rs b/src/parser/element.rs index ecf9a80..e3a2c4e 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -8,6 +8,7 @@ use super::greater_element::DynamicBlock; use super::greater_element::FootnoteDefinition; use super::greater_element::GreaterBlock; use super::greater_element::PlainList; +use super::greater_element::PropertyDrawer; use super::lesser_element::Comment; use super::lesser_element::Paragraph; use super::paragraph::paragraph; @@ -29,6 +30,7 @@ pub enum Element<'s> { FootnoteDefinition(FootnoteDefinition<'s>), Comment(Comment<'s>), Drawer(Drawer<'s>), + PropertyDrawer(PropertyDrawer<'s>), } impl<'s> Source<'s> for Element<'s> { @@ -41,6 +43,7 @@ impl<'s> Source<'s> for Element<'s> { Element::FootnoteDefinition(obj) => obj.source, Element::Comment(obj) => obj.source, Element::Drawer(obj) => obj.source, + Element::PropertyDrawer(obj) => obj.source, } } } @@ -93,6 +96,12 @@ impl<'s> Source<'s> for Drawer<'s> { } } +impl<'s> Source<'s> for PropertyDrawer<'s> { + fn get_source(&'s self) -> &'s str { + self.source + } +} + #[tracing::instrument(ret, level = "debug")] pub fn element<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Element<'s>> { let non_paragraph_matcher = parser_with_context!(non_paragraph_element)(context); diff --git a/src/parser/greater_element.rs b/src/parser/greater_element.rs index 9018726..c40d85c 100644 --- a/src/parser/greater_element.rs +++ b/src/parser/greater_element.rs @@ -43,3 +43,10 @@ pub struct Drawer<'s> { pub name: &'s str, pub children: Vec>, } + +#[derive(Debug)] +pub struct PropertyDrawer<'s> { + pub source: &'s str, + pub name: &'s str, + pub children: Vec>, +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3a2eb61..6359a5c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -16,6 +16,7 @@ mod parser_context; mod parser_with_context; mod plain_list; mod plain_text; +mod property_drawer; mod source; mod util; pub use document::document; @@ -30,6 +31,7 @@ pub use greater_element::FootnoteDefinition; pub use greater_element::GreaterBlock; pub use greater_element::PlainList; pub use greater_element::PlainListItem; +pub use greater_element::PropertyDrawer; pub use lesser_element::Comment; pub use lesser_element::Paragraph; pub use source::Source; diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs new file mode 100644 index 0000000..31f2ee5 --- /dev/null +++ b/src/parser/property_drawer.rs @@ -0,0 +1,11 @@ +use super::Context; +use crate::parser::error::Res; +use crate::parser::greater_element::PropertyDrawer; + +#[tracing::instrument(ret, level = "debug")] +pub fn property_drawer<'r, 's>( + context: Context<'r, 's>, + input: &'s str, +) -> Res<&'s str, PropertyDrawer<'s>> { + todo!() +} From 18f8e2562e7b377bbdb064b600562eb70475ccb4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 16:54:17 -0400 Subject: [PATCH 03/14] Remove the old toy language binary. Using the org_mode_samples integration tests through the rust test framework has removed the need for the toy language binary and I think removing it will make rust-analyzer less confused about imports. This repository is now solely a library repository. --- Cargo.toml | 5 ---- src/main.rs | 17 ------------ src/parser/property_drawer.rs | 52 +++++++++++++++++++++++++++++++++++ toy_language.txt | 5 ---- 4 files changed, 52 insertions(+), 27 deletions(-) delete mode 100644 src/main.rs delete mode 100644 toy_language.txt diff --git a/Cargo.toml b/Cargo.toml index 7f42a24..4dd767a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,16 +3,11 @@ name = "toy" version = "0.1.0" edition = "2021" license = "0BSD" -default-run = "toy" [lib] name = "organic" path = "src/lib.rs" -[[bin]] -name = "toy" -path = "src/main.rs" - [dependencies] nom = "7.1.1" opentelemetry = "0.17.0" diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 6201ac9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(round_char_boundary)] -use crate::init_tracing::init_telemetry; -use crate::init_tracing::shutdown_telemetry; -use crate::parser::document; -mod init_tracing; -mod parser; - -const TEST_DOC: &'static str = include_str!("../toy_language.txt"); - -fn main() -> Result<(), Box> { - init_telemetry()?; - let parsed = document(TEST_DOC); - println!("{}\n\n\n", TEST_DOC); - println!("{:#?}", parsed); - shutdown_telemetry()?; - Ok(()) -} diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index 31f2ee5..0b5132f 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -1,11 +1,63 @@ use super::Context; +use crate::parser::error::CustomError; +use crate::parser::error::MyError; use crate::parser::error::Res; use crate::parser::greater_element::PropertyDrawer; +use crate::parser::util::immediate_in_section; #[tracing::instrument(ret, level = "debug")] pub fn property_drawer<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, PropertyDrawer<'s>> { + if immediate_in_section(context, "property-drawer") { + return Err(nom::Err::Error(CustomError::MyError(MyError( + "Cannot nest objects of the same element", + )))); + } + // start_of_line(context, input)?; + // let (remaining, _leading_whitespace) = space0(input)?; + // let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( + // tag(":"), + // name, + // tag(":"), + // recognize(tuple((space0, line_ending))), + // ))(remaining)?; + + // let parser_context = context + // .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) + // .with_additional_node(ContextElement::Context("drawer")) + // .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + // class: ExitClass::Alpha, + // exit_matcher: &drawer_end, + // })); + + // let element_matcher = parser_with_context!(element)(&parser_context); + // let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); + // let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) { + // Ok((remaining, (whitespace, (_children, _exit_contents)))) => ( + // remaining, + // vec![Element::Paragraph(Paragraph::of_text(whitespace))], + // ), + // Err(_) => { + // let (remaining, (children, _exit_contents)) = + // many_till(element_matcher, exit_matcher)(remaining)?; + // (remaining, children) + // } + // }; + // let (remaining, _end) = drawer_end(&parser_context, remaining)?; + + // let (remaining, _trailing_ws) = + // maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + // let source = get_consumed(input, remaining); + + // Ok(( + // remaining, + // Drawer { + // source, + // name: drawer_name, + // children, + // }, + // )) todo!() } diff --git a/toy_language.txt b/toy_language.txt deleted file mode 100644 index 574a2f2..0000000 --- a/toy_language.txt +++ /dev/null @@ -1,5 +0,0 @@ -foo -:drawername: - - -:end: From 2137b08e425d07e10895d0ae7874f59634a33090 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 17:03:46 -0400 Subject: [PATCH 04/14] Detect the opening and build the context. --- src/parser/property_drawer.rs | 57 ++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index 0b5132f..c17eabd 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -1,9 +1,23 @@ +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::bytes::complete::tag_no_case; +use nom::character::complete::line_ending; +use nom::character::complete::space0; +use nom::combinator::eof; +use nom::combinator::recognize; +use nom::sequence::tuple; + use super::Context; use crate::parser::error::CustomError; use crate::parser::error::MyError; use crate::parser::error::Res; +use crate::parser::exiting::ExitClass; use crate::parser::greater_element::PropertyDrawer; +use crate::parser::parser_context::ContextElement; +use crate::parser::parser_context::ExitMatcherNode; +use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::immediate_in_section; +use crate::parser::util::start_of_line; #[tracing::instrument(ret, level = "debug")] pub fn property_drawer<'r, 's>( @@ -15,22 +29,24 @@ pub fn property_drawer<'r, 's>( "Cannot nest objects of the same element", )))); } - // start_of_line(context, input)?; - // let (remaining, _leading_whitespace) = space0(input)?; - // let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( - // tag(":"), - // name, - // tag(":"), - // recognize(tuple((space0, line_ending))), - // ))(remaining)?; + let ( + remaining, + (_start_of_line, _leading_whitespace, open_tag, _trailing_whitespace, _line_ending), + ) = tuple(( + parser_with_context!(start_of_line)(context), + space0, + tag_no_case(":PROPERTIES:"), + space0, + line_ending, + ))(input)?; - // let parser_context = context - // .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) - // .with_additional_node(ContextElement::Context("drawer")) - // .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - // class: ExitClass::Alpha, - // exit_matcher: &drawer_end, - // })); + let parser_context = context + .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) + .with_additional_node(ContextElement::Context("property-drawer")) + .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Alpha, + exit_matcher: &property_drawer_end, + })); // let element_matcher = parser_with_context!(element)(&parser_context); // let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); @@ -61,3 +77,14 @@ pub fn property_drawer<'r, 's>( // )) todo!() } + +#[tracing::instrument(ret, level = "debug")] +fn property_drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { + recognize(tuple(( + parser_with_context!(start_of_line)(context), + space0, + tag(":end:"), + space0, + alt((line_ending, eof)), + )))(input) +} From f7f29e7a566e45231794ca0b7cc19c2e42e4fc17 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 17:15:00 -0400 Subject: [PATCH 05/14] Finish property drawer parser but node property is unstarted. --- org_mode_samples/property_drawer/empty.org | 3 ++ src/parser/element.rs | 3 ++ src/parser/greater_element.rs | 9 +++- src/parser/property_drawer.rs | 48 ++++++++++------------ 4 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 org_mode_samples/property_drawer/empty.org diff --git a/org_mode_samples/property_drawer/empty.org b/org_mode_samples/property_drawer/empty.org new file mode 100644 index 0000000..d86fd5b --- /dev/null +++ b/org_mode_samples/property_drawer/empty.org @@ -0,0 +1,3 @@ +:PROPERTIES: + +:END: diff --git a/src/parser/element.rs b/src/parser/element.rs index e3a2c4e..2938884 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -13,6 +13,7 @@ use super::lesser_element::Comment; use super::lesser_element::Paragraph; use super::paragraph::paragraph; use super::plain_list::plain_list; +use super::property_drawer::property_drawer; use super::source::Source; use super::Context; use super::Drawer; @@ -122,6 +123,7 @@ pub fn non_paragraph_element<'r, 's>( let dynamic_block_matcher = parser_with_context!(dynamic_block)(context); let footnote_definition_matcher = parser_with_context!(footnote_definition)(context); let comment_matcher = parser_with_context!(comment)(context); + let property_drawer_matcher = parser_with_context!(property_drawer)(context); let drawer_matcher = parser_with_context!(drawer)(context); alt(( map(plain_list_matcher, Element::PlainList), @@ -129,6 +131,7 @@ pub fn non_paragraph_element<'r, 's>( map(dynamic_block_matcher, Element::DynamicBlock), map(footnote_definition_matcher, Element::FootnoteDefinition), map(comment_matcher, Element::Comment), + map(property_drawer_matcher, Element::PropertyDrawer), map(drawer_matcher, Element::Drawer), ))(input) } diff --git a/src/parser/greater_element.rs b/src/parser/greater_element.rs index c40d85c..172056a 100644 --- a/src/parser/greater_element.rs +++ b/src/parser/greater_element.rs @@ -47,6 +47,13 @@ pub struct Drawer<'s> { #[derive(Debug)] pub struct PropertyDrawer<'s> { pub source: &'s str, - pub name: &'s str, + pub children: Vec>, +} + +#[derive(Debug)] +pub struct NodeProperty<'s> { + pub source: &'s str, + pub indentation: usize, + pub bullet: &'s str, pub children: Vec>, } diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index c17eabd..a106e4b 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -5,6 +5,7 @@ use nom::character::complete::line_ending; use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::recognize; +use nom::multi::many_till; use nom::sequence::tuple; use super::Context; @@ -12,11 +13,15 @@ use crate::parser::error::CustomError; use crate::parser::error::MyError; use crate::parser::error::Res; use crate::parser::exiting::ExitClass; +use crate::parser::greater_element::NodeProperty; use crate::parser::greater_element::PropertyDrawer; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; +use crate::parser::util::exit_matcher_parser; +use crate::parser::util::get_consumed; use crate::parser::util::immediate_in_section; +use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::parser::util::start_of_line; #[tracing::instrument(ret, level = "debug")] @@ -48,34 +53,17 @@ pub fn property_drawer<'r, 's>( exit_matcher: &property_drawer_end, })); - // let element_matcher = parser_with_context!(element)(&parser_context); - // let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - // let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) { - // Ok((remaining, (whitespace, (_children, _exit_contents)))) => ( - // remaining, - // vec![Element::Paragraph(Paragraph::of_text(whitespace))], - // ), - // Err(_) => { - // let (remaining, (children, _exit_contents)) = - // many_till(element_matcher, exit_matcher)(remaining)?; - // (remaining, children) - // } - // }; - // let (remaining, _end) = drawer_end(&parser_context, remaining)?; + let element_matcher = parser_with_context!(node_property)(&parser_context); + let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); + let (remaining, (children, _exit_contents)) = + many_till(element_matcher, exit_matcher)(remaining)?; + let (remaining, _end) = property_drawer_end(&parser_context, remaining)?; - // let (remaining, _trailing_ws) = - // maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - // let source = get_consumed(input, remaining); + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + let source = get_consumed(input, remaining); - // Ok(( - // remaining, - // Drawer { - // source, - // name: drawer_name, - // children, - // }, - // )) - todo!() + Ok((remaining, PropertyDrawer { source, children })) } #[tracing::instrument(ret, level = "debug")] @@ -88,3 +76,11 @@ fn property_drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res< alt((line_ending, eof)), )))(input) } + +#[tracing::instrument(ret, level = "debug")] +pub fn node_property<'r, 's>( + context: Context<'r, 's>, + input: &'s str, +) -> Res<&'s str, NodeProperty<'s>> { + todo!() +} From 8de6f1a81784e71333a777accee318e10d0e0e9e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 18:10:29 -0400 Subject: [PATCH 06/14] Implement node property. --- src/parser/greater_element.rs | 4 +- src/parser/property_drawer.rs | 70 +++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/parser/greater_element.rs b/src/parser/greater_element.rs index 172056a..78b05c2 100644 --- a/src/parser/greater_element.rs +++ b/src/parser/greater_element.rs @@ -53,7 +53,5 @@ pub struct PropertyDrawer<'s> { #[derive(Debug)] pub struct NodeProperty<'s> { pub source: &'s str, - pub indentation: usize, - pub bullet: &'s str, - pub children: Vec>, + pub value: Option<&'s str>, } diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index a106e4b..8af5137 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -1,9 +1,13 @@ use nom::branch::alt; +use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; use nom::character::complete::line_ending; use nom::character::complete::space0; +use nom::character::complete::space1; use nom::combinator::eof; +use nom::combinator::map; +use nom::combinator::opt; use nom::combinator::recognize; use nom::multi::many_till; use nom::sequence::tuple; @@ -18,6 +22,7 @@ use crate::parser::greater_element::PropertyDrawer; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; +use crate::parser::plain_text::plain_text; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::util::immediate_in_section; @@ -53,10 +58,10 @@ pub fn property_drawer<'r, 's>( exit_matcher: &property_drawer_end, })); - let element_matcher = parser_with_context!(node_property)(&parser_context); + let node_property_matcher = parser_with_context!(node_property)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let (remaining, (children, _exit_contents)) = - many_till(element_matcher, exit_matcher)(remaining)?; + many_till(node_property_matcher, exit_matcher)(remaining)?; let (remaining, _end) = property_drawer_end(&parser_context, remaining)?; let (remaining, _trailing_ws) = @@ -77,10 +82,67 @@ fn property_drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res< )))(input) } -#[tracing::instrument(ret, level = "debug")] pub fn node_property<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, NodeProperty<'s>> { - todo!() + let (remaining, (_start_of_line, _leading_whitespace, _open_colon, name, _close_colon)) = + tuple(( + parser_with_context!(start_of_line)(context), + space0, + tag(":"), + parser_with_context!(node_property_name)(context), + tag(":"), + ))(input)?; + match tuple((space0::<&str, nom::error::Error<&str>>, line_ending))(remaining) { + Ok((remaining, _ws)) => { + let source = get_consumed(input, remaining); + Ok(( + remaining, + NodeProperty { + source, + value: None, + }, + )) + } + Err(_) => { + let (remaining, (_ws, value)) = tuple((space1, is_not("\r\n")))(remaining)?; + let source = get_consumed(input, remaining); + Ok(( + remaining, + NodeProperty { + source, + value: Some(value), + }, + )) + } + } +} + +#[tracing::instrument(ret, level = "debug")] +pub fn node_property_name<'r, 's>( + context: Context<'r, 's>, + input: &'s str, +) -> Res<&'s str, &'s str> { + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Alpha, + exit_matcher: &node_property_name_end, + })); + + let (remaining, name) = recognize(tuple(( + map(parser_with_context!(plain_text)(&parser_context), |pt| { + pt.source + }), + opt(tag("+")), + )))(input)?; + Ok((remaining, name)) +} + +#[tracing::instrument(ret, level = "debug")] +pub fn node_property_name_end<'r, 's>( + context: Context<'r, 's>, + input: &'s str, +) -> Res<&'s str, &'s str> { + alt((tag("+:"), tag(":")))(input) } From 99677ee98ed3cc41e640f32f4a0297efe51442d4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 18:10:55 -0400 Subject: [PATCH 07/14] fixup --- src/parser/property_drawer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index 8af5137..c6ad8f5 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -126,7 +126,7 @@ pub fn node_property_name<'r, 's>( ) -> Res<&'s str, &'s str> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Alpha, + class: ExitClass::Beta, exit_matcher: &node_property_name_end, })); From 1deb53f1a330595a1294b7b6f23688177cf3c490 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 18:12:53 -0400 Subject: [PATCH 08/14] Add the binary back in because we need it for generating jaeger traces. --- Cargo.toml | 5 +++++ src/main.rs | 17 +++++++++++++++++ toy_language.txt | 5 +++++ 3 files changed, 27 insertions(+) create mode 100644 src/main.rs create mode 100644 toy_language.txt diff --git a/Cargo.toml b/Cargo.toml index 4dd767a..7f42a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,16 @@ name = "toy" version = "0.1.0" edition = "2021" license = "0BSD" +default-run = "toy" [lib] name = "organic" path = "src/lib.rs" +[[bin]] +name = "toy" +path = "src/main.rs" + [dependencies] nom = "7.1.1" opentelemetry = "0.17.0" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6201ac9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +#![feature(round_char_boundary)] +use crate::init_tracing::init_telemetry; +use crate::init_tracing::shutdown_telemetry; +use crate::parser::document; +mod init_tracing; +mod parser; + +const TEST_DOC: &'static str = include_str!("../toy_language.txt"); + +fn main() -> Result<(), Box> { + init_telemetry()?; + let parsed = document(TEST_DOC); + println!("{}\n\n\n", TEST_DOC); + println!("{:#?}", parsed); + shutdown_telemetry()?; + Ok(()) +} diff --git a/toy_language.txt b/toy_language.txt new file mode 100644 index 0000000..574a2f2 --- /dev/null +++ b/toy_language.txt @@ -0,0 +1,5 @@ +foo +:drawername: + + +:end: From b557b9ca5dada44c7057d11f738437e3ecd82298 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 18:37:39 -0400 Subject: [PATCH 09/14] Accounting for the blank lines at the head of documents and comments plus property drawer at the head of the zeroth section. --- Cargo.toml | 1 - .../property_drawer/zeroth_section.org | 17 ++++++++ src/parser/document.rs | 41 ++++++++++++++++++- toy_language.txt | 9 ++-- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f42a24..9e11b28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ name = "toy" version = "0.1.0" edition = "2021" license = "0BSD" -default-run = "toy" [lib] name = "organic" diff --git a/org_mode_samples/property_drawer/zeroth_section.org b/org_mode_samples/property_drawer/zeroth_section.org index ccdae6f..2538054 100644 --- a/org_mode_samples/property_drawer/zeroth_section.org +++ b/org_mode_samples/property_drawer/zeroth_section.org @@ -6,3 +6,20 @@ :PROPERTIES: :FOO: bar :END: + + + +* Spaces turn property drawers into regular drawers + +:PROPERTIES: +:FOO: bar +:END: +* Comments turn property drawers into regular drawers +# Comment +:PROPERTIES: +:FOO: bar +:END: +* Baseline +:PROPERTIES: +:FOO: bar +:END: diff --git a/src/parser/document.rs b/src/parser/document.rs index f2e4308..309e2ae 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -1,3 +1,4 @@ +use crate::parser::property_drawer::property_drawer; use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::line_ending; @@ -13,12 +14,14 @@ use nom::multi::many1_count; use nom::multi::many_till; use nom::sequence::tuple; +use crate::parser::comment::comment; use crate::parser::element::element; use crate::parser::exiting::ExitClass; use crate::parser::object::standard_set_object; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ExitMatcherNode; +use crate::parser::util::blank_line; use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::element::Element; @@ -92,9 +95,10 @@ pub fn document(input: &str) -> Res<&str, Document> { let initial_context: ContextTree<'_, '_> = ContextTree::new(); let document_context = initial_context.with_additional_node(ContextElement::DocumentRoot(input)); - let section_matcher = parser_with_context!(section)(&document_context); + let zeroth_section_matcher = parser_with_context!(zeroth_section)(&document_context); let heading_matcher = parser_with_context!(heading)(&document_context); - let (remaining, zeroth_section) = opt(section_matcher)(input)?; + let (remaining, _blank_lines) = many0(blank_line)(input)?; + let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?; let (remaining, children) = many0(heading_matcher)(remaining)?; let source = get_consumed(input, remaining); Ok(( @@ -107,6 +111,39 @@ pub fn document(input: &str) -> Res<&str, Document> { )) } +#[tracing::instrument(ret, level = "debug")] +fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Section<'s>> { + // TODO: The zeroth section is specialized so it probably needs its own parser + let parser_context = context + .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) + .with_additional_node(ContextElement::Context("section")) + .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Document, + exit_matcher: §ion_end, + })); + let element_matcher = parser_with_context!(element)(&parser_context); + let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); + + let (remaining, (comment_element, property_drawer_element)) = tuple(( + opt(parser_with_context!(comment)(&parser_context)), + opt(parser_with_context!(property_drawer)(&parser_context)), + ))(input)?; + + let (remaining, (mut children, _exit_contents)) = verify( + many_till(element_matcher, exit_matcher), + |(children, _exit_contents)| !children.is_empty(), + )(remaining)?; + property_drawer_element.map(Element::PropertyDrawer).map(|ele| children.insert(0, ele)); + comment_element.map(Element::Comment).map(|ele| children.insert(0, ele)); + // children.insert(0, item) + + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + + let source = get_consumed(input, remaining); + Ok((remaining, Section { source, children })) +} + #[tracing::instrument(ret, level = "debug")] fn section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Section<'s>> { // TODO: The zeroth section is specialized so it probably needs its own parser diff --git a/toy_language.txt b/toy_language.txt index 574a2f2..ccdae6f 100644 --- a/toy_language.txt +++ b/toy_language.txt @@ -1,5 +1,8 @@ -foo -:drawername: -:end: + + +# Blank lines and comments can come before property drawers in the zeroth section +:PROPERTIES: +:FOO: bar +:END: From 603cc131a0ce20ede7d74c3bf00a6cc5e0a1bd13 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 19:03:51 -0400 Subject: [PATCH 10/14] Numerous fixes to property drawers. --- src/parser/document.rs | 34 ++++++++++++++++++++++++++-------- src/parser/element.rs | 3 --- src/parser/property_drawer.rs | 15 +++++++-------- toy_language.txt | 4 ++++ 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/parser/document.rs b/src/parser/document.rs index 309e2ae..77ed1a0 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -121,20 +121,33 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s class: ExitClass::Document, exit_matcher: §ion_end, })); + let without_consuming_whitespace_context = + parser_context.with_additional_node(ContextElement::ConsumeTrailingWhitespace(false)); + let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let (remaining, (comment_element, property_drawer_element)) = tuple(( - opt(parser_with_context!(comment)(&parser_context)), - opt(parser_with_context!(property_drawer)(&parser_context)), + opt(parser_with_context!(comment)( + &without_consuming_whitespace_context, + )), + opt(parser_with_context!(property_drawer)( + &without_consuming_whitespace_context, + )), ))(input)?; let (remaining, (mut children, _exit_contents)) = verify( many_till(element_matcher, exit_matcher), - |(children, _exit_contents)| !children.is_empty(), + |(children, _exit_contents)| { + !children.is_empty() || property_drawer_element.is_some() || comment_element.is_some() + }, )(remaining)?; - property_drawer_element.map(Element::PropertyDrawer).map(|ele| children.insert(0, ele)); - comment_element.map(Element::Comment).map(|ele| children.insert(0, ele)); + property_drawer_element + .map(Element::PropertyDrawer) + .map(|ele| children.insert(0, ele)); + comment_element + .map(Element::Comment) + .map(|ele| children.insert(0, ele)); // children.insert(0, item) let (remaining, _trailing_ws) = @@ -156,10 +169,15 @@ fn section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Sec })); let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - let (remaining, (children, _exit_contents)) = verify( + let (remaining, property_drawer_element) = + opt(parser_with_context!(property_drawer)(&parser_context))(input)?; + let (remaining, (mut children, _exit_contents)) = verify( many_till(element_matcher, exit_matcher), - |(children, _exit_contents)| !children.is_empty(), - )(input)?; + |(children, _exit_contents)| !children.is_empty() || property_drawer_element.is_some(), + )(remaining)?; + property_drawer_element + .map(Element::PropertyDrawer) + .map(|ele| children.insert(0, ele)); let (remaining, _trailing_ws) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; diff --git a/src/parser/element.rs b/src/parser/element.rs index 2938884..e3a2c4e 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -13,7 +13,6 @@ use super::lesser_element::Comment; use super::lesser_element::Paragraph; use super::paragraph::paragraph; use super::plain_list::plain_list; -use super::property_drawer::property_drawer; use super::source::Source; use super::Context; use super::Drawer; @@ -123,7 +122,6 @@ pub fn non_paragraph_element<'r, 's>( let dynamic_block_matcher = parser_with_context!(dynamic_block)(context); let footnote_definition_matcher = parser_with_context!(footnote_definition)(context); let comment_matcher = parser_with_context!(comment)(context); - let property_drawer_matcher = parser_with_context!(property_drawer)(context); let drawer_matcher = parser_with_context!(drawer)(context); alt(( map(plain_list_matcher, Element::PlainList), @@ -131,7 +129,6 @@ pub fn non_paragraph_element<'r, 's>( map(dynamic_block_matcher, Element::DynamicBlock), map(footnote_definition_matcher, Element::FootnoteDefinition), map(comment_matcher, Element::Comment), - map(property_drawer_matcher, Element::PropertyDrawer), map(drawer_matcher, Element::Drawer), ))(input) } diff --git a/src/parser/property_drawer.rs b/src/parser/property_drawer.rs index c6ad8f5..f8283c6 100644 --- a/src/parser/property_drawer.rs +++ b/src/parser/property_drawer.rs @@ -76,13 +76,14 @@ fn property_drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res< recognize(tuple(( parser_with_context!(start_of_line)(context), space0, - tag(":end:"), + tag_no_case(":end:"), space0, alt((line_ending, eof)), )))(input) } -pub fn node_property<'r, 's>( +#[tracing::instrument(ret, level = "debug")] +fn node_property<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, NodeProperty<'s>> { @@ -106,7 +107,8 @@ pub fn node_property<'r, 's>( )) } Err(_) => { - let (remaining, (_ws, value)) = tuple((space1, is_not("\r\n")))(remaining)?; + let (remaining, (_ws, value, _line_ending)) = + tuple((space1, is_not("\r\n"), line_ending))(remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, @@ -120,10 +122,7 @@ pub fn node_property<'r, 's>( } #[tracing::instrument(ret, level = "debug")] -pub fn node_property_name<'r, 's>( - context: Context<'r, 's>, - input: &'s str, -) -> Res<&'s str, &'s str> { +fn node_property_name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Beta, @@ -140,7 +139,7 @@ pub fn node_property_name<'r, 's>( } #[tracing::instrument(ret, level = "debug")] -pub fn node_property_name_end<'r, 's>( +fn node_property_name_end<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, &'s str> { diff --git a/toy_language.txt b/toy_language.txt index ccdae6f..e432ad0 100644 --- a/toy_language.txt +++ b/toy_language.txt @@ -3,6 +3,10 @@ # Blank lines and comments can come before property drawers in the zeroth section + + + + :PROPERTIES: :FOO: bar :END: From 4e65d2cda9f42746ca62991a66aeb68bd51cae68 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 19:22:23 -0400 Subject: [PATCH 11/14] Fix requiring no space between comment and property drawer for zeroth section. --- src/parser/document.rs | 24 +++++++++++------------- src/parser/drawer.rs | 3 ++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/parser/document.rs b/src/parser/document.rs index 77ed1a0..ddaa283 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -127,28 +127,26 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - let (remaining, (comment_element, property_drawer_element)) = tuple(( + let (remaining, comment_and_property_drawer_element) = opt(tuple(( opt(parser_with_context!(comment)( &without_consuming_whitespace_context, )), - opt(parser_with_context!(property_drawer)( - &without_consuming_whitespace_context, - )), - ))(input)?; + parser_with_context!(property_drawer)(&without_consuming_whitespace_context), + )))(input)?; let (remaining, (mut children, _exit_contents)) = verify( many_till(element_matcher, exit_matcher), |(children, _exit_contents)| { - !children.is_empty() || property_drawer_element.is_some() || comment_element.is_some() + !children.is_empty() || comment_and_property_drawer_element.is_some() }, )(remaining)?; - property_drawer_element - .map(Element::PropertyDrawer) - .map(|ele| children.insert(0, ele)); - comment_element - .map(Element::Comment) - .map(|ele| children.insert(0, ele)); - // children.insert(0, item) + + comment_and_property_drawer_element.map(|(comment, property_drawer)| { + children.insert(0, Element::PropertyDrawer(property_drawer)); + comment + .map(Element::Comment) + .map(|ele| children.insert(0, ele)); + }); let (remaining, _trailing_ws) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; diff --git a/src/parser/drawer.rs b/src/parser/drawer.rs index d8f2f87..e79cdeb 100644 --- a/src/parser/drawer.rs +++ b/src/parser/drawer.rs @@ -1,5 +1,6 @@ use nom::branch::alt; use nom::bytes::complete::tag; +use nom::bytes::complete::tag_no_case; use nom::bytes::complete::take_while; use nom::character::complete::line_ending; use nom::character::complete::space0; @@ -92,7 +93,7 @@ fn drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, start_of_line(context, input)?; recognize(tuple(( space0, - tag(":end:"), + tag_no_case(":end:"), space0, alt((line_ending, eof)), )))(input) From d0410391cc260bdcc1f6ff4ad962c1494ecab046 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 19:28:21 -0400 Subject: [PATCH 12/14] Drop blank lines after property drawer instead of owning them. --- src/parser/document.rs | 3 ++- toy_language.txt | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/parser/document.rs b/src/parser/document.rs index ddaa283..e57710e 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -132,6 +132,7 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s &without_consuming_whitespace_context, )), parser_with_context!(property_drawer)(&without_consuming_whitespace_context), + many0(blank_line), )))(input)?; let (remaining, (mut children, _exit_contents)) = verify( @@ -141,7 +142,7 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s }, )(remaining)?; - comment_and_property_drawer_element.map(|(comment, property_drawer)| { + comment_and_property_drawer_element.map(|(comment, property_drawer, _ws)| { children.insert(0, Element::PropertyDrawer(property_drawer)); comment .map(Element::Comment) diff --git a/toy_language.txt b/toy_language.txt index e432ad0..2538054 100644 --- a/toy_language.txt +++ b/toy_language.txt @@ -3,10 +3,23 @@ # Blank lines and comments can come before property drawers in the zeroth section +:PROPERTIES: +:FOO: bar +:END: +* Spaces turn property drawers into regular drawers :PROPERTIES: :FOO: bar :END: +* Comments turn property drawers into regular drawers +# Comment +:PROPERTIES: +:FOO: bar +:END: +* Baseline +:PROPERTIES: +:FOO: bar +:END: From 87bdedf2e6034078c45611046afbff169b803160 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 19:39:56 -0400 Subject: [PATCH 13/14] Only drop trailing whitespace when there isn't a property drawer. --- src/parser/document.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/parser/document.rs b/src/parser/document.rs index e57710e..6d2e2da 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -157,7 +157,7 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s } #[tracing::instrument(ret, level = "debug")] -fn section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Section<'s>> { +fn section<'r, 's>(context: Context<'r, 's>, mut input: &'s str) -> Res<&'s str, Section<'s>> { // TODO: The zeroth section is specialized so it probably needs its own parser let parser_context = context .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) @@ -168,8 +168,13 @@ fn section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Sec })); let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - let (remaining, property_drawer_element) = + let (mut remaining, property_drawer_element) = opt(parser_with_context!(property_drawer)(&parser_context))(input)?; + if property_drawer_element.is_none() { + let (remain, _ws) = many0(blank_line)(remaining)?; + remaining = remain; + input = remain; + } let (remaining, (mut children, _exit_contents)) = verify( many_till(element_matcher, exit_matcher), |(children, _exit_contents)| !children.is_empty() || property_drawer_element.is_some(), @@ -194,7 +199,7 @@ fn section_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, #[tracing::instrument(ret, level = "debug")] fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Heading<'s>> { not(|i| context.check_exit_matcher(i))(input)?; - let (remaining, (star_count, _ws, title, _ws2)) = headline(context, input)?; + let (remaining, (star_count, _ws, title)) = headline(context, input)?; let section_matcher = parser_with_context!(section)(context); let heading_matcher = parser_with_context!(heading)(context); let (remaining, children) = many0(alt(( @@ -220,7 +225,7 @@ fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Hea fn headline<'r, 's>( context: Context<'r, 's>, input: &'s str, -) -> Res<&'s str, (usize, &'s str, Vec>, &'s str)> { +) -> Res<&'s str, (usize, &'s str, Vec>)> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Document, @@ -229,14 +234,13 @@ fn headline<'r, 's>( let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); let start_of_line_matcher = parser_with_context!(start_of_line)(&parser_context); - let (remaining, (_sol, star_count, ws, title, ws2)) = tuple(( + let (remaining, (_sol, star_count, ws, title)) = tuple(( start_of_line_matcher, many1_count(tag("*")), space1, many1(standard_set_object_matcher), - trailing_whitespace, ))(input)?; - Ok((remaining, (star_count, ws, title, ws2))) + Ok((remaining, (star_count, ws, title))) } #[tracing::instrument(ret, level = "debug")] From 7728f3b71ecb28f95999713e14edd7a7236abd11 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 19 Apr 2023 19:53:37 -0400 Subject: [PATCH 14/14] Fix property drawers beneath headlines. --- src/parser/document.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/parser/document.rs b/src/parser/document.rs index 6d2e2da..b2e3541 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -1,8 +1,18 @@ +use crate::parser::comment::comment; +use crate::parser::element::element; +use crate::parser::exiting::ExitClass; +use crate::parser::object::standard_set_object; +use crate::parser::parser_context::ContextElement; +use crate::parser::parser_context::ContextTree; +use crate::parser::parser_context::ExitMatcherNode; use crate::parser::property_drawer::property_drawer; +use crate::parser::util::blank_line; +use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::line_ending; use nom::character::complete::space1; +use nom::combinator::eof; use nom::combinator::map; use nom::combinator::not; use nom::combinator::opt; @@ -14,16 +24,6 @@ use nom::multi::many1_count; use nom::multi::many_till; use nom::sequence::tuple; -use crate::parser::comment::comment; -use crate::parser::element::element; -use crate::parser::exiting::ExitClass; -use crate::parser::object::standard_set_object; -use crate::parser::parser_context::ContextElement; -use crate::parser::parser_context::ContextTree; -use crate::parser::parser_context::ExitMatcherNode; -use crate::parser::util::blank_line; -use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; - use super::element::Element; use super::error::Res; use super::object::Object; @@ -234,11 +234,12 @@ fn headline<'r, 's>( let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); let start_of_line_matcher = parser_with_context!(start_of_line)(&parser_context); - let (remaining, (_sol, star_count, ws, title)) = tuple(( + let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple(( start_of_line_matcher, many1_count(tag("*")), space1, many1(standard_set_object_matcher), + alt((line_ending, eof)), ))(input)?; Ok((remaining, (star_count, ws, title))) }