diff --git a/build.rs b/build.rs index c8b3c1dd..284d86df 100644 --- a/build.rs +++ b/build.rs @@ -75,6 +75,7 @@ fn is_expect_fail(name: &str) -> Option<&str> { "autogen_greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."), "autogen_element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."), "autogen_lesser_element_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."), + "autogen_sections_and_headings_empty_section" => Some("We are not yet handling empty sections properly."), _ => None, } } diff --git a/elisp_snippets/README.md b/elisp_snippets/README.md new file mode 100644 index 00000000..e21bab12 --- /dev/null +++ b/elisp_snippets/README.md @@ -0,0 +1 @@ +This folder is for snippets of elisp that are useful for development. diff --git a/elisp_snippets/dump_org_entities.el b/elisp_snippets/dump_org_entities.el new file mode 100644 index 00000000..530d6c3b --- /dev/null +++ b/elisp_snippets/dump_org_entities.el @@ -0,0 +1,5 @@ +(dolist (var org-entities) + (when (listp var) + (message "\"%s\"," (nth 0 var)) + ) + ) diff --git a/org_mode_samples/object/citation/balanced_brackets.org b/org_mode_samples/object/citation/balanced_brackets.org new file mode 100644 index 00000000..08036b75 --- /dev/null +++ b/org_mode_samples/object/citation/balanced_brackets.org @@ -0,0 +1,22 @@ +# Extra open +[cite/a/b-_/foo:unbalancedglobal[prefix;keyprefix @foo keysuffix;globalsuffix] + +[cite/a/b-_/foo:globalprefix;unbalancedkey[prefix @foo keysuffix;globalsuffix] + +[cite/a/b-_/foo:globalprefix;keyprefix @foo unbalancedkey[suffix;globalsuffix] + +[cite/a/b-_/foo:globalprefix;keyprefix @foo keysuffix;unbalancedglobal[suffix] + + +# Extra close +[cite/a/b-_/foo:unbalancedglobal]prefix;keyprefix @foo keysuffix;globalsuffix] + +[cite/a/b-_/foo:globalprefix;unbalancedkey]prefix @foo keysuffix;globalsuffix] + +[cite/a/b-_/foo:globalprefix;keyprefix @foo unbalancedkey]suffix;globalsuffix] + +[cite/a/b-_/foo:globalprefix;keyprefix @foo keysuffix;unbalancedglobal]suffix] + + +# balanced: +[cite/a/b-_/foo:gl[obalpref]ix;ke[ypref]ix @foo ke[ysuff]ix;gl[obalsuff]ix] diff --git a/org_mode_samples/object/footnote_reference/balanced_brackets.org b/org_mode_samples/object/footnote_reference/balanced_brackets.org new file mode 100644 index 00000000..251c9add --- /dev/null +++ b/org_mode_samples/object/footnote_reference/balanced_brackets.org @@ -0,0 +1,2 @@ +[fn:2:This footnote [ has balanced ] brackets inside it] +[fn::This footnote does not have balanced [ brackets inside it] diff --git a/org_mode_samples/object/latex_fragment/three_lines.org b/org_mode_samples/object/latex_fragment/three_lines.org new file mode 100644 index 00000000..af68c597 --- /dev/null +++ b/org_mode_samples/object/latex_fragment/three_lines.org @@ -0,0 +1,6 @@ +$foo +bar +baz +lorem +ipsum +dolar$ diff --git a/org_mode_samples/object/text_markup/three_lines.org b/org_mode_samples/object/text_markup/three_lines.org new file mode 100644 index 00000000..06925fe8 --- /dev/null +++ b/org_mode_samples/object/text_markup/three_lines.org @@ -0,0 +1,17 @@ +foo *bar +baz* lorem + +text *markup +can +span* more + +than *three +lines. +foo +bar* baz + +foo *bar \\ +baz \\ +lorem \\ +ipsum \\ +dolar* cat diff --git a/org_mode_samples/sections_and_headings/empty_section.org b/org_mode_samples/sections_and_headings/empty_section.org new file mode 100644 index 00000000..6e45bf8c --- /dev/null +++ b/org_mode_samples/sections_and_headings/empty_section.org @@ -0,0 +1,9 @@ +* Foo + +* Bar + + + + + +* Baz diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 5c22d63a..fda1ef5e 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -48,6 +48,7 @@ use crate::parser::RadioLink; use crate::parser::RadioTarget; use crate::parser::RegularLink; use crate::parser::Section; +use crate::parser::Source; use crate::parser::SrcBlock; use crate::parser::StatisticsCookie; use crate::parser::StrikeThrough; @@ -63,12 +64,14 @@ use crate::parser::Verbatim; use crate::parser::VerseBlock; #[derive(Debug)] -pub struct DiffResult { +pub struct DiffResult<'s> { status: DiffStatus, name: String, - #[allow(dead_code)] message: Option, - children: Vec, + children: Vec>, + rust_source: &'s str, + #[allow(dead_code)] + emacs_token: &'s Token<'s>, } #[derive(Debug, PartialEq)] @@ -77,12 +80,16 @@ pub enum DiffStatus { Bad, } -impl DiffResult { - pub fn print(&self) -> Result<(), Box> { - self.print_indented(0) +impl<'s> DiffResult<'s> { + pub fn print(&self, original_document: &str) -> Result<(), Box> { + self.print_indented(0, original_document) } - fn print_indented(&self, indentation: usize) -> Result<(), Box> { + fn print_indented( + &self, + indentation: usize, + original_document: &str, + ) -> Result<(), Box> { let status_text = { match self.status { DiffStatus::Good => { @@ -107,15 +114,17 @@ impl DiffResult { ), } }; + let rust_offset = self.rust_source.as_ptr() as usize - original_document.as_ptr() as usize; println!( - "{}{} {} {}", - " ".repeat(indentation), - status_text, - self.name, - self.message.as_ref().map(|m| m.as_str()).unwrap_or("") + "{indentation}{status_text} {name} char({char_offset}) {message}", + indentation = " ".repeat(indentation), + status_text = status_text, + name = self.name, + char_offset = rust_offset, + message = self.message.as_ref().map(|m| m.as_str()).unwrap_or("") ); for child in self.children.iter() { - child.print_indented(indentation + 1)?; + child.print_indented(indentation + 1, original_document)?; } Ok(()) } @@ -177,8 +186,8 @@ fn compare_element<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Element<'s>, -) -> Result> { - match rust { +) -> Result, Box> { + let compare_result = match rust { Element::Paragraph(obj) => compare_paragraph(source, emacs, obj), Element::PlainList(obj) => compare_plain_list(source, emacs, obj), Element::GreaterBlock(obj) => compare_greater_block(source, emacs, obj), @@ -200,6 +209,17 @@ fn compare_element<'s>( Element::HorizontalRule(obj) => compare_horizontal_rule(source, emacs, obj), Element::Keyword(obj) => compare_keyword(source, emacs, obj), Element::LatexEnvironment(obj) => compare_latex_environment(source, emacs, obj), + }; + match compare_result { + Ok(_) => compare_result, + Err(ref e) => Ok(DiffResult { + status: DiffStatus::Bad, + name: "error!".to_owned(), + message: Some(e.to_string()), + children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, + }), } } @@ -207,8 +227,8 @@ fn compare_object<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Object<'s>, -) -> Result> { - match rust { +) -> Result, Box> { + let compare_result = match rust { Object::Bold(obj) => compare_bold(source, emacs, obj), Object::Italic(obj) => compare_italic(source, emacs, obj), Object::Underline(obj) => compare_underline(source, emacs, obj), @@ -236,13 +256,24 @@ fn compare_object<'s>( Object::Subscript(obj) => compare_subscript(source, emacs, obj), Object::Superscript(obj) => compare_superscript(source, emacs, obj), Object::Timestamp(obj) => compare_timestamp(source, emacs, obj), + }; + match compare_result { + Ok(_) => compare_result, + Err(ref e) => Ok(DiffResult { + status: DiffStatus::Bad, + name: "error!".to_owned(), + message: Some(e.to_string()), + children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, + }), } } pub fn compare_document<'s>( emacs: &'s Token<'s>, rust: &'s Document<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -286,6 +317,8 @@ pub fn compare_document<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -293,7 +326,7 @@ fn compare_section<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Section<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); @@ -315,6 +348,8 @@ fn compare_section<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -322,7 +357,7 @@ fn compare_heading<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Heading<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -410,6 +445,8 @@ fn compare_heading<'s>( name: emacs_name.to_owned(), message, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -450,7 +487,7 @@ fn compare_paragraph<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Paragraph<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -472,6 +509,8 @@ fn compare_paragraph<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -479,7 +518,7 @@ fn compare_plain_list<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s PlainList<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -501,6 +540,8 @@ fn compare_plain_list<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -508,7 +549,7 @@ fn compare_plain_list_item<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s PlainListItem<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -530,6 +571,8 @@ fn compare_plain_list_item<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -537,7 +580,7 @@ fn compare_greater_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s GreaterBlock<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -563,6 +606,8 @@ fn compare_greater_block<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -570,7 +615,7 @@ fn compare_dynamic_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s DynamicBlock<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -591,6 +636,8 @@ fn compare_dynamic_block<'s>( name: "dynamic-block".to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -598,7 +645,7 @@ fn compare_footnote_definition<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s FootnoteDefinition<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -620,6 +667,8 @@ fn compare_footnote_definition<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -627,7 +676,7 @@ fn compare_comment<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Comment<'s>, -) -> Result> { +) -> Result, Box> { let child_status = Vec::new(); let mut this_status = DiffStatus::Good; let emacs_name = "comment"; @@ -644,6 +693,8 @@ fn compare_comment<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -651,7 +702,7 @@ fn compare_drawer<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Drawer<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -673,6 +724,8 @@ fn compare_drawer<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -680,7 +733,7 @@ fn compare_property_drawer<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s PropertyDrawer<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -703,6 +756,8 @@ fn compare_property_drawer<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -710,7 +765,7 @@ fn compare_table<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Table<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -732,6 +787,8 @@ fn compare_table<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -739,7 +796,7 @@ fn compare_table_row<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s TableRow<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -761,6 +818,8 @@ fn compare_table_row<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -768,7 +827,7 @@ fn compare_table_cell<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s TableCell<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -788,6 +847,8 @@ fn compare_table_cell<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -795,7 +856,7 @@ fn compare_verse_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s VerseBlock<'s>, -) -> Result> { +) -> Result, Box> { let children = emacs.as_list()?; let child_status = Vec::new(); let mut this_status = DiffStatus::Good; @@ -815,6 +876,8 @@ fn compare_verse_block<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -822,7 +885,7 @@ fn compare_comment_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s CommentBlock<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "comment-block"; if assert_name(emacs, emacs_name).is_err() { @@ -838,6 +901,8 @@ fn compare_comment_block<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -845,7 +910,7 @@ fn compare_example_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s ExampleBlock<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "example-block"; if assert_name(emacs, emacs_name).is_err() { @@ -861,6 +926,8 @@ fn compare_example_block<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -868,7 +935,7 @@ fn compare_export_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s ExportBlock<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "export-block"; if assert_name(emacs, emacs_name).is_err() { @@ -884,6 +951,8 @@ fn compare_export_block<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -891,7 +960,7 @@ fn compare_src_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s SrcBlock<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "src-block"; if assert_name(emacs, emacs_name).is_err() { @@ -907,6 +976,8 @@ fn compare_src_block<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -914,7 +985,7 @@ fn compare_clock<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Clock<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "clock"; if assert_name(emacs, emacs_name).is_err() { @@ -930,6 +1001,8 @@ fn compare_clock<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -937,7 +1010,7 @@ fn compare_diary_sexp<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s DiarySexp<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "diary-sexp"; if assert_name(emacs, emacs_name).is_err() { @@ -953,6 +1026,8 @@ fn compare_diary_sexp<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -960,7 +1035,7 @@ fn compare_planning<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Planning<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "planning"; if assert_name(emacs, emacs_name).is_err() { @@ -976,6 +1051,8 @@ fn compare_planning<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -983,7 +1060,7 @@ fn compare_fixed_width_area<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s FixedWidthArea<'s>, -) -> Result> { +) -> Result, Box> { let child_status = Vec::new(); let mut this_status = DiffStatus::Good; let emacs_name = "fixed-width"; @@ -1000,6 +1077,8 @@ fn compare_fixed_width_area<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1007,7 +1086,7 @@ fn compare_horizontal_rule<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s HorizontalRule<'s>, -) -> Result> { +) -> Result, Box> { let child_status = Vec::new(); let mut this_status = DiffStatus::Good; let emacs_name = "horizontal-rule"; @@ -1024,6 +1103,8 @@ fn compare_horizontal_rule<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1031,7 +1112,7 @@ fn compare_keyword<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Keyword<'s>, -) -> Result> { +) -> Result, Box> { let child_status = Vec::new(); let mut this_status = DiffStatus::Good; let emacs_name = "keyword"; @@ -1048,6 +1129,8 @@ fn compare_keyword<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1055,7 +1138,7 @@ fn compare_latex_environment<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s LatexEnvironment<'s>, -) -> Result> { +) -> Result, Box> { let child_status = Vec::new(); let mut this_status = DiffStatus::Good; let emacs_name = "latex-environment"; @@ -1072,6 +1155,8 @@ fn compare_latex_environment<'s>( name: emacs_name.to_owned(), message: None, children: child_status, + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1079,7 +1164,7 @@ fn compare_plain_text<'s>( _source: &'s str, emacs: &'s Token<'s>, rust: &'s PlainText<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut message = None; let text = emacs.as_text()?; @@ -1118,6 +1203,8 @@ fn compare_plain_text<'s>( name: "plain-text".to_owned(), message, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1125,7 +1212,7 @@ fn compare_bold<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Bold<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "bold"; if assert_name(emacs, emacs_name).is_err() { @@ -1141,6 +1228,8 @@ fn compare_bold<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1148,7 +1237,7 @@ fn compare_italic<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Italic<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "italic"; if assert_name(emacs, emacs_name).is_err() { @@ -1164,6 +1253,8 @@ fn compare_italic<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1171,7 +1262,7 @@ fn compare_underline<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Underline<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "underline"; if assert_name(emacs, emacs_name).is_err() { @@ -1187,6 +1278,8 @@ fn compare_underline<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1194,7 +1287,7 @@ fn compare_verbatim<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Verbatim<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "verbatim"; if assert_name(emacs, emacs_name).is_err() { @@ -1210,6 +1303,8 @@ fn compare_verbatim<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1217,7 +1312,7 @@ fn compare_code<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Code<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "code"; if assert_name(emacs, emacs_name).is_err() { @@ -1233,6 +1328,8 @@ fn compare_code<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1240,7 +1337,7 @@ fn compare_strike_through<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s StrikeThrough<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "strike-through"; if assert_name(emacs, emacs_name).is_err() { @@ -1256,6 +1353,8 @@ fn compare_strike_through<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1263,7 +1362,7 @@ fn compare_regular_link<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s RegularLink<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "link"; if assert_name(emacs, emacs_name).is_err() { @@ -1279,6 +1378,8 @@ fn compare_regular_link<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1286,7 +1387,7 @@ fn compare_radio_link<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s RadioLink<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "link"; if assert_name(emacs, emacs_name).is_err() { @@ -1302,6 +1403,8 @@ fn compare_radio_link<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1309,7 +1412,7 @@ fn compare_radio_target<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s RadioTarget<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "radio-target"; if assert_name(emacs, emacs_name).is_err() { @@ -1325,6 +1428,8 @@ fn compare_radio_target<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1332,7 +1437,7 @@ fn compare_plain_link<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s PlainLink<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "link"; if assert_name(emacs, emacs_name).is_err() { @@ -1348,6 +1453,8 @@ fn compare_plain_link<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1355,7 +1462,7 @@ fn compare_angle_link<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s AngleLink<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "link"; if assert_name(emacs, emacs_name).is_err() { @@ -1371,6 +1478,8 @@ fn compare_angle_link<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1378,7 +1487,7 @@ fn compare_org_macro<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s OrgMacro<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "macro"; if assert_name(emacs, emacs_name).is_err() { @@ -1394,6 +1503,8 @@ fn compare_org_macro<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1401,7 +1512,7 @@ fn compare_entity<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Entity<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "entity"; if assert_name(emacs, emacs_name).is_err() { @@ -1417,6 +1528,8 @@ fn compare_entity<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1424,7 +1537,7 @@ fn compare_latex_fragment<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s LatexFragment<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "latex-fragment"; if assert_name(emacs, emacs_name).is_err() { @@ -1440,6 +1553,8 @@ fn compare_latex_fragment<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1447,7 +1562,7 @@ fn compare_export_snippet<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s ExportSnippet<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "export-snippet"; if assert_name(emacs, emacs_name).is_err() { @@ -1463,6 +1578,8 @@ fn compare_export_snippet<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1470,7 +1587,7 @@ fn compare_footnote_reference<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s FootnoteReference<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "footnote-reference"; if assert_name(emacs, emacs_name).is_err() { @@ -1486,6 +1603,8 @@ fn compare_footnote_reference<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1493,7 +1612,7 @@ fn compare_citation<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Citation<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "citation"; if assert_name(emacs, emacs_name).is_err() { @@ -1509,6 +1628,8 @@ fn compare_citation<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1516,7 +1637,7 @@ fn compare_citation_reference<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s CitationReference<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "citation-reference"; if assert_name(emacs, emacs_name).is_err() { @@ -1532,6 +1653,8 @@ fn compare_citation_reference<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1539,7 +1662,7 @@ fn compare_inline_babel_call<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s InlineBabelCall<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "inline-babel-call"; if assert_name(emacs, emacs_name).is_err() { @@ -1555,6 +1678,8 @@ fn compare_inline_babel_call<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1562,7 +1687,7 @@ fn compare_inline_source_block<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s InlineSourceBlock<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "inline-src-block"; if assert_name(emacs, emacs_name).is_err() { @@ -1578,6 +1703,8 @@ fn compare_inline_source_block<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1585,7 +1712,7 @@ fn compare_line_break<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s LineBreak<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "line-break"; if assert_name(emacs, emacs_name).is_err() { @@ -1601,6 +1728,8 @@ fn compare_line_break<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1608,7 +1737,7 @@ fn compare_target<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Target<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "target"; if assert_name(emacs, emacs_name).is_err() { @@ -1624,6 +1753,8 @@ fn compare_target<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1631,7 +1762,7 @@ fn compare_statistics_cookie<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s StatisticsCookie<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "statistics-cookie"; if assert_name(emacs, emacs_name).is_err() { @@ -1647,6 +1778,8 @@ fn compare_statistics_cookie<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1654,7 +1787,7 @@ fn compare_subscript<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Subscript<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "subscript"; if assert_name(emacs, emacs_name).is_err() { @@ -1670,6 +1803,8 @@ fn compare_subscript<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1677,7 +1812,7 @@ fn compare_superscript<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Superscript<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "superscript"; if assert_name(emacs, emacs_name).is_err() { @@ -1693,6 +1828,8 @@ fn compare_superscript<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } @@ -1700,7 +1837,7 @@ fn compare_timestamp<'s>( source: &'s str, emacs: &'s Token<'s>, rust: &'s Timestamp<'s>, -) -> Result> { +) -> Result, Box> { let mut this_status = DiffStatus::Good; let emacs_name = "timestamp"; if assert_name(emacs, emacs_name).is_err() { @@ -1716,5 +1853,7 @@ fn compare_timestamp<'s>( name: emacs_name.to_owned(), message: None, children: Vec::new(), + rust_source: rust.get_source(), + emacs_token: emacs, }) } diff --git a/src/main.rs b/src/main.rs index 9d0c27df..ba9f535c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,20 +55,21 @@ fn read_stdin_to_string() -> Result> { fn run_compare>(org_contents: P) -> Result<(), Box> { let emacs_version = get_emacs_version()?; let org_mode_version = get_org_mode_version()?; + let org_contents = org_contents.as_ref(); eprintln!("Using emacs version: {}", emacs_version.trim()); eprintln!("Using org-mode version: {}", org_mode_version.trim()); - let (remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?; - let org_sexp = emacs_parse_org_document(org_contents.as_ref())?; + let (remaining, rust_parsed) = document(org_contents).map_err(|e| e.to_string())?; + let org_sexp = emacs_parse_org_document(org_contents)?; let (_remaining, parsed_sexp) = sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?; - println!("{}\n\n\n", org_contents.as_ref()); + println!("{}\n\n\n", org_contents); println!("{}", org_sexp); println!("{:#?}", rust_parsed); // We do the diffing after printing out both parsed forms in case the diffing panics let diff_result = compare_document(&parsed_sexp, &rust_parsed)?; - diff_result.print()?; + diff_result.print(org_contents)?; if diff_result.is_bad() { Err("Diff results do not match.")?; diff --git a/src/parser/citation.rs b/src/parser/citation.rs index ffce49a1..13acca0e 100644 --- a/src/parser/citation.rs +++ b/src/parser/citation.rs @@ -11,17 +11,17 @@ use nom::multi::many_till; use nom::multi::separated_list1; use nom::sequence::tuple; +use super::citation_reference::must_balance_bracket; +use super::org_source::BracketDepth; use super::org_source::OrgSource; use super::Context; use crate::error::CustomError; use crate::error::Res; use crate::parser::citation_reference::citation_reference; use crate::parser::citation_reference::citation_reference_key; -use crate::parser::citation_reference::get_bracket_depth; use crate::parser::exiting::ExitClass; use crate::parser::object::Citation; use crate::parser::object_parser::standard_set_object; -use crate::parser::parser_context::CitationBracket; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; @@ -38,13 +38,15 @@ pub fn citation<'r, 's>( let (remaining, _) = tag_no_case("[cite")(input)?; let (remaining, _) = opt(citestyle)(remaining)?; let (remaining, _) = tag(":")(remaining)?; - let (remaining, _prefix) = opt(parser_with_context!(global_prefix)(context))(remaining)?; + let (remaining, _prefix) = + must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?; + let (remaining, _references) = separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?; - let (remaining, _suffix) = opt(tuple(( + let (remaining, _suffix) = must_balance_bracket(opt(tuple(( tag(";"), parser_with_context!(global_suffix)(context), - )))(remaining)?; + ))))(remaining)?; let (remaining, _) = tag("]")(remaining)?; let (remaining, _) = space0(remaining)?; let source = get_consumed(input, remaining); @@ -83,15 +85,11 @@ fn global_prefix<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Vec>> { - // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. - let parser_context = context - .with_additional_node(ContextElement::CitationBracket(CitationBracket { - position: input, - depth: 0, - })) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = global_prefix_end(input.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &global_prefix_end, + exit_matcher: &exit_with_depth, })); let (remaining, (children, _exit_contents)) = verify( many_till( @@ -104,28 +102,24 @@ fn global_prefix<'r, 's>( Ok((remaining, children)) } +fn global_prefix_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _global_prefix_end(context, input, starting_bracket_depth) + } +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn global_prefix_end<'r, 's>( +fn _global_prefix_end<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, ) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside a citation."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded citation global prefix bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } + let current_depth = input.get_bracket_depth() - starting_bracket_depth; + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation. + unreachable!("Exceeded citation global prefix bracket depth.") } if current_depth == 0 { let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("]")(input); @@ -144,15 +138,11 @@ fn global_suffix<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Vec>> { - // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. - let parser_context = context - .with_additional_node(ContextElement::CitationBracket(CitationBracket { - position: input, - depth: 0, - })) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = global_suffix_end(input.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &global_suffix_end, + exit_matcher: &exit_with_depth, })); let (remaining, (children, _exit_contents)) = verify( many_till( @@ -164,28 +154,24 @@ fn global_suffix<'r, 's>( Ok((remaining, children)) } +fn global_suffix_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _global_suffix_end(context, input, starting_bracket_depth) + } +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn global_suffix_end<'r, 's>( +fn _global_suffix_end<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, ) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside a citation."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded citation global suffix bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } + let current_depth = input.get_bracket_depth() - starting_bracket_depth; + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation. + unreachable!("Exceeded citation global suffix bracket depth.") } if current_depth == 0 { let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("]")(input); diff --git a/src/parser/citation_reference.rs b/src/parser/citation_reference.rs index b854f6f9..0024fc3c 100644 --- a/src/parser/citation_reference.rs +++ b/src/parser/citation_reference.rs @@ -10,14 +10,15 @@ use nom::multi::many_till; use nom::sequence::preceded; use nom::sequence::tuple; +use super::org_source::BracketDepth; use super::org_source::OrgSource; use super::Context; use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::exiting::ExitClass; use crate::parser::object::CitationReference; use crate::parser::object_parser::minimal_set_object; -use crate::parser::parser_context::CitationBracket; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; @@ -31,9 +32,11 @@ pub fn citation_reference<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, CitationReference<'s>> { - let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?; + let (remaining, _prefix) = + must_balance_bracket(opt(parser_with_context!(key_prefix)(context)))(input)?; let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?; - let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?; + let (remaining, _suffix) = + must_balance_bracket(opt(parser_with_context!(key_suffix)(context)))(remaining)?; let source = get_consumed(input, remaining); Ok(( @@ -69,15 +72,11 @@ fn key_prefix<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Vec>> { - // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. - let parser_context = context - .with_additional_node(ContextElement::CitationBracket(CitationBracket { - position: input, - depth: 0, - })) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = key_prefix_end(input.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &key_prefix_end, + exit_matcher: &exit_with_depth, })); let (remaining, (children, _exit_contents)) = verify( many_till( @@ -94,15 +93,11 @@ fn key_suffix<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Vec>> { - // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. - let parser_context = context - .with_additional_node(ContextElement::CitationBracket(CitationBracket { - position: input, - depth: 0, - })) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = key_suffix_end(input.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &key_suffix_end, + exit_matcher: &exit_with_depth, })); let (remaining, (children, _exit_contents)) = verify( many_till( @@ -114,39 +109,24 @@ fn key_suffix<'r, 's>( Ok((remaining, children)) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r CitationBracket<'s>> { - for node in context.iter() { - match node.get_data() { - ContextElement::CitationBracket(depth) => return Some(depth), - _ => {} - } +fn key_prefix_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _key_prefix_end(context, input, starting_bracket_depth) } - None } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn key_prefix_end<'r, 's>( +fn _key_prefix_end<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, ) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside a citation reference."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded citation reference key prefix bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } + let current_depth = input.get_bracket_depth() - starting_bracket_depth; + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation. + unreachable!("Exceeded citation key prefix bracket depth.") } if current_depth == 0 { let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("]")(input); @@ -160,28 +140,24 @@ fn key_prefix_end<'r, 's>( ))(input) } +fn key_suffix_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _key_suffix_end(context, input, starting_bracket_depth) + } +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn key_suffix_end<'r, 's>( - context: Context<'r, 's>, +fn _key_suffix_end<'r, 's>( + _context: Context<'r, 's>, input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, ) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside a citation reference."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded citation reference key prefix bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } + let current_depth = input.get_bracket_depth() - starting_bracket_depth; + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the citation. + unreachable!("Exceeded citation key suffix bracket depth.") } if current_depth == 0 { let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("]")(input); @@ -191,3 +167,21 @@ fn key_suffix_end<'r, 's>( } tag(";")(input) } + +pub fn must_balance_bracket<'s, F, O>( + mut inner: F, +) -> impl FnMut(OrgSource<'s>) -> Res, O> +where + F: FnMut(OrgSource<'s>) -> Res, O>, +{ + move |input: OrgSource<'_>| { + let pre_bracket_depth = input.get_bracket_depth(); + let (remaining, output) = inner(input)?; + if remaining.get_bracket_depth() - pre_bracket_depth != 0 { + return Err(nom::Err::Error(CustomError::MyError(MyError( + "UnbalancedBrackets".into(), + )))); + } + Ok((remaining, output)) + } +} diff --git a/src/parser/document.rs b/src/parser/document.rs index 8fd86248..efb85d51 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -139,7 +139,7 @@ fn _document<'r, 's>( input: OrgSource<'s>, ) -> Res, Document<'s>> { let zeroth_section_matcher = parser_with_context!(zeroth_section)(context); - let heading_matcher = parser_with_context!(heading)(context); + let heading_matcher = parser_with_context!(heading(0))(context); let (remaining, _blank_lines) = many0(blank_line)(input)?; let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?; let (remaining, children) = many0(heading_matcher)(remaining)?; @@ -260,28 +260,31 @@ fn section<'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn section_end<'r, 's>( - context: Context<'r, 's>, + _context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - let headline_matcher = parser_with_context!(headline)(context); - recognize(headline_matcher)(input) + recognize(detect_headline)(input) +} + +const fn heading( + parent_stars: usize, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, Heading<'s>> { + move |context: Context, input: OrgSource<'_>| _heading(context, input, parent_stars) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn heading<'r, 's>( +fn _heading<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + parent_stars: usize, ) -> Res, Heading<'s>> { not(|i| context.check_exit_matcher(i))(input)?; let (remaining, (star_count, _ws, maybe_todo_keyword, title, heading_tags)) = - headline(context, input)?; + headline(context, input, parent_stars)?; let section_matcher = parser_with_context!(section)(context); - let heading_matcher = parser_with_context!(heading)(context); + let heading_matcher = parser_with_context!(heading(star_count))(context); let (remaining, children) = many0(alt(( - map( - verify(heading_matcher, |h| h.stars > star_count), - DocumentElement::Heading, - ), + map(heading_matcher, DocumentElement::Heading), map(section_matcher, DocumentElement::Section), )))(remaining)?; let source = get_consumed(input, remaining); @@ -299,10 +302,17 @@ fn heading<'r, 's>( )) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn detect_headline<'s>(input: OrgSource<'s>) -> Res, ()> { + tuple((start_of_line, many1(tag("*")), space1))(input)?; + Ok((input, ())) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn headline<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + parent_stars: usize, ) -> Res< OrgSource<'s>, ( @@ -325,7 +335,9 @@ fn headline<'r, 's>( (_sol, star_count, ws, maybe_todo_keyword, title, maybe_tags, _ws, _line_ending), ) = tuple(( start_of_line, - many1_count(tag("*")), + verify(many1_count(tag("*")), |star_count| { + *star_count > parent_stars + }), space1, opt(tuple((heading_keyword, space1))), many1(standard_set_object_matcher), diff --git a/src/parser/entity.rs b/src/parser/entity.rs index 1774d58b..8bd7186e 100644 --- a/src/parser/entity.rs +++ b/src/parser/entity.rs @@ -9,11 +9,428 @@ use nom::combinator::recognize; use super::org_source::OrgSource; use super::Context; +use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::object::Entity; -use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::get_consumed; +const ENTITIES: [&'static str; 413] = [ + "Agrave", + "agrave", + "Aacute", + "aacute", + "Acirc", + "acirc", + "Amacr", + "amacr", + "Atilde", + "atilde", + "Auml", + "auml", + "Aring", + "AA", + "aring", + "AElig", + "aelig", + "Ccedil", + "ccedil", + "Egrave", + "egrave", + "Eacute", + "eacute", + "Ecirc", + "ecirc", + "Euml", + "euml", + "Igrave", + "igrave", + "Iacute", + "iacute", + "Idot", + "inodot", + "Icirc", + "icirc", + "Iuml", + "iuml", + "Ntilde", + "ntilde", + "Ograve", + "ograve", + "Oacute", + "oacute", + "Ocirc", + "ocirc", + "Otilde", + "otilde", + "Ouml", + "ouml", + "Oslash", + "oslash", + "OElig", + "oelig", + "Scaron", + "scaron", + "szlig", + "Ugrave", + "ugrave", + "Uacute", + "uacute", + "Ucirc", + "ucirc", + "Uuml", + "uuml", + "Yacute", + "yacute", + "Yuml", + "yuml", + "fnof", + "real", + "image", + "weierp", + "ell", + "imath", + "jmath", + "Alpha", + "alpha", + "Beta", + "beta", + "Gamma", + "gamma", + "Delta", + "delta", + "Epsilon", + "epsilon", + "varepsilon", + "Zeta", + "zeta", + "Eta", + "eta", + "Theta", + "theta", + "thetasym", + "vartheta", + "Iota", + "iota", + "Kappa", + "kappa", + "Lambda", + "lambda", + "Mu", + "mu", + "nu", + "Nu", + "Xi", + "xi", + "Omicron", + "omicron", + "Pi", + "pi", + "Rho", + "rho", + "Sigma", + "sigma", + "sigmaf", + "varsigma", + "Tau", + "Upsilon", + "upsih", + "upsilon", + "Phi", + "phi", + "varphi", + "Chi", + "chi", + "acutex", + "Psi", + "psi", + "tau", + "Omega", + "omega", + "piv", + "varpi", + "partial", + "alefsym", + "aleph", + "gimel", + "beth", + "dalet", + "ETH", + "eth", + "THORN", + "thorn", + "dots", + "cdots", + "hellip", + "middot", + "iexcl", + "iquest", + "shy", + "ndash", + "mdash", + "quot", + "acute", + "ldquo", + "rdquo", + "bdquo", + "lsquo", + "rsquo", + "sbquo", + "laquo", + "raquo", + "lsaquo", + "rsaquo", + "circ", + "vert", + "vbar", + "brvbar", + "S", + "sect", + "amp", + "lt", + "gt", + "tilde", + "slash", + "plus", + "under", + "equal", + "asciicirc", + "dagger", + "dag", + "Dagger", + "ddag", + "nbsp", + "ensp", + "emsp", + "thinsp", + "curren", + "cent", + "pound", + "yen", + "euro", + "EUR", + "dollar", + "USD", + "copy", + "reg", + "trade", + "minus", + "pm", + "plusmn", + "times", + "frasl", + "colon", + "div", + "frac12", + "frac14", + "frac34", + "permil", + "sup1", + "sup2", + "sup3", + "radic", + "sum", + "prod", + "micro", + "macr", + "deg", + "prime", + "Prime", + "infin", + "infty", + "prop", + "propto", + "not", + "neg", + "land", + "wedge", + "lor", + "vee", + "cap", + "cup", + "smile", + "frown", + "int", + "therefore", + "there4", + "because", + "sim", + "cong", + "simeq", + "asymp", + "approx", + "ne", + "neq", + "equiv", + "triangleq", + "le", + "leq", + "ge", + "geq", + "lessgtr", + "lesseqgtr", + "ll", + "Ll", + "lll", + "gg", + "Gg", + "ggg", + "prec", + "preceq", + "preccurlyeq", + "succ", + "succeq", + "succcurlyeq", + "sub", + "subset", + "sup", + "supset", + "nsub", + "sube", + "nsup", + "supe", + "setminus", + "forall", + "exist", + "exists", + "nexist", + "nexists", + "empty", + "emptyset", + "isin", + "in", + "notin", + "ni", + "nabla", + "ang", + "angle", + "perp", + "parallel", + "sdot", + "cdot", + "lceil", + "rceil", + "lfloor", + "rfloor", + "lang", + "rang", + "langle", + "rangle", + "hbar", + "mho", + "larr", + "leftarrow", + "gets", + "lArr", + "Leftarrow", + "uarr", + "uparrow", + "uArr", + "Uparrow", + "rarr", + "to", + "rightarrow", + "rArr", + "Rightarrow", + "darr", + "downarrow", + "dArr", + "Downarrow", + "harr", + "leftrightarrow", + "hArr", + "Leftrightarrow", + "crarr", + "hookleftarrow", + "arccos", + "arcsin", + "arctan", + "arg", + "cos", + "cosh", + "cot", + "coth", + "csc", + "deg", + "det", + "dim", + "exp", + "gcd", + "hom", + "inf", + "ker", + "lg", + "lim", + "liminf", + "limsup", + "ln", + "log", + "max", + "min", + "Pr", + "sec", + "sin", + "sinh", + "sup", + "tan", + "tanh", + "bull", + "bullet", + "star", + "lowast", + "ast", + "odot", + "oplus", + "otimes", + "check", + "checkmark", + "para", + "ordf", + "ordm", + "cedil", + "oline", + "uml", + "zwnj", + "zwj", + "lrm", + "rlm", + "smiley", + "blacksmile", + "sad", + "frowny", + "clubs", + "clubsuit", + "spades", + "spadesuit", + "hearts", + "heartsuit", + "diams", + "diamondsuit", + "diamond", + "Diamond", + "loz", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", + "_ ", +]; + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn entity<'r, 's>( context: Context<'r, 's>, @@ -21,10 +438,7 @@ pub fn entity<'r, 's>( ) -> Res, Entity<'s>> { let (remaining, _) = tag("\\")(input)?; let (remaining, entity_name) = name(context, remaining)?; - let (remaining, _) = alt(( - tag("{}"), - peek(recognize(parser_with_context!(entity_end)(context))), - ))(remaining)?; + let (remaining, _) = alt((tag("{}"), peek(recognize(entity_end))))(remaining)?; let (remaining, _) = space0(remaining)?; let source = get_consumed(input, remaining); @@ -43,20 +457,24 @@ fn name<'r, 's>( input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { // TODO: This should be defined by org-entities and optionally org-entities-user + for entity in ENTITIES { + // foo + let result = tag_no_case::<_, _, CustomError<_>>(entity)(input); + match result { + Ok((remaining, ent)) => { + return Ok((remaining, ent)); + } + Err(_) => {} + } + } - // TODO: Add the rest of the entities, this is a very incomplete list - let (remaining, proto) = alt((alt(( - tag_no_case("delta"), - tag_no_case("pi"), - tag_no_case("ast"), - tag_no_case("lt"), - tag_no_case("gt"), - )),))(input)?; - Ok((remaining, proto)) + Err(nom::Err::Error(CustomError::MyError(MyError( + "NoEntity".into(), + )))) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn entity_end<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res, ()> { +fn entity_end<'s>(input: OrgSource<'s>) -> Res, ()> { let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?; Ok((remaining, ())) diff --git a/src/parser/footnote_reference.rs b/src/parser/footnote_reference.rs index ad35b9ac..2de9a126 100644 --- a/src/parser/footnote_reference.rs +++ b/src/parser/footnote_reference.rs @@ -5,6 +5,7 @@ use nom::character::complete::space0; use nom::combinator::verify; use nom::multi::many_till; +use super::org_source::BracketDepth; use super::org_source::OrgSource; use super::parser_context::ContextElement; use super::Context; @@ -15,7 +16,6 @@ use crate::parser::exiting::ExitClass; use crate::parser::footnote_definition::label; use crate::parser::object_parser::standard_set_object; use crate::parser::parser_context::ExitMatcherNode; -use crate::parser::parser_context::FootnoteReferenceDefinition; use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; @@ -39,18 +39,12 @@ fn anonymous_footnote<'r, 's>( input: OrgSource<'s>, ) -> Res, FootnoteReference<'s>> { let (remaining, _) = tag_no_case("[fn::")(input)?; - let parser_context = context - .with_additional_node(ContextElement::FootnoteReferenceDefinition( - FootnoteReferenceDefinition { - position: remaining, - depth: 0, - }, - )) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = footnote_definition_end(remaining.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Beta, - exit_matcher: &footnote_definition_end, + exit_matcher: &exit_with_depth, })); - // TODO: I could insert FootnoteReferenceDefinition entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. let (remaining, (children, _exit_contents)) = verify( many_till( parser_with_context!(standard_set_object)(&parser_context), @@ -80,18 +74,12 @@ fn inline_footnote<'r, 's>( let (remaining, _) = tag_no_case("[fn:")(input)?; let (remaining, label_contents) = label(remaining)?; let (remaining, _) = tag(":")(remaining)?; - let parser_context = context - .with_additional_node(ContextElement::FootnoteReferenceDefinition( - FootnoteReferenceDefinition { - position: remaining, - depth: 0, - }, - )) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = footnote_definition_end(remaining.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Beta, - exit_matcher: &footnote_definition_end, + exit_matcher: &exit_with_depth, })); - // TODO: I could insert FootnoteReferenceDefinition entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. let (remaining, (children, _exit_contents)) = verify( many_till( parser_with_context!(standard_set_object)(&parser_context), @@ -133,47 +121,30 @@ fn footnote_reference_only<'r, 's>( )) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn footnote_definition_end<'r, 's>( - context: Context<'r, 's>, - input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside a footnote definition."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded footnote reference definition bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } +fn footnote_definition_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _footnote_definition_end(context, input, starting_bracket_depth) } +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn _footnote_definition_end<'r, 's>( + _context: Context<'r, 's>, + input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, +) -> Res, OrgSource<'s>> { + let current_depth = input.get_bracket_depth() - starting_bracket_depth; if current_depth > 0 { // Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep return Err(nom::Err::Error(CustomError::MyError(MyError( "NoFootnoteReferenceDefinitionEnd".into(), )))); } + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the footnote definition. + unreachable!("Exceeded footnote reference definition bracket depth.") + } tag("]")(input) } - -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn get_bracket_depth<'r, 's>( - context: Context<'r, 's>, -) -> Option<&'r FootnoteReferenceDefinition<'s>> { - for node in context.iter() { - match node.get_data() { - ContextElement::FootnoteReferenceDefinition(depth) => return Some(depth), - _ => {} - } - } - None -} diff --git a/src/parser/greater_block.rs b/src/parser/greater_block.rs index fa1ca690..85dc7e35 100644 --- a/src/parser/greater_block.rs +++ b/src/parser/greater_block.rs @@ -58,15 +58,15 @@ pub fn greater_block<'r, 's>( "Cannot nest objects of the same element".into(), )))); } + let exit_with_name = greater_block_end(name.into()); let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?; let (remaining, _nl) = line_ending(remaining)?; let parser_context = context .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::Context(context_name)) - .with_additional_node(ContextElement::GreaterBlock(name.into())) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Alpha, - exit_matcher: &greater_block_end, + exit_matcher: &exit_with_name, })); let parameters = match parameters { Some((_ws, parameters)) => Some(parameters), @@ -94,7 +94,7 @@ pub fn greater_block<'r, 's>( (remaining, children) } }; - let (remaining, _end) = greater_block_end(&parser_context, remaining)?; + let (remaining, _end) = exit_with_name(&parser_context, remaining)?; // Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block @@ -120,31 +120,27 @@ fn parameters<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { is_not("\r\n")(input) } +fn greater_block_end<'x>( + name: &'x str, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + // TODO: Can this be done without making an owned copy? + let name = name.to_owned(); + move |context: Context, input: OrgSource<'_>| _greater_block_end(context, input, name.as_str()) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn greater_block_end<'r, 's>( - context: Context<'r, 's>, +fn _greater_block_end<'r, 's, 'x>( + _context: Context<'r, 's>, input: OrgSource<'s>, + name: &'x str, ) -> Res, OrgSource<'s>> { start_of_line(input)?; - let current_name: &str = get_context_greater_block_name(context).ok_or(nom::Err::Error( - CustomError::MyError(MyError("Not inside a greater block".into())), - ))?; let (remaining, _leading_whitespace) = space0(input)?; let (remaining, (_begin, _name, _ws)) = tuple(( tag_no_case("#+end_"), - tag_no_case(current_name), + tag_no_case(name), alt((eof, line_ending)), ))(remaining)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } - -fn get_context_greater_block_name<'r, 's>(context: Context<'r, 's>) -> Option<&'s str> { - for thing in context.iter() { - match thing.get_data() { - ContextElement::GreaterBlock(name) => return Some(name), - _ => {} - }; - } - None -} diff --git a/src/parser/inline_babel_call.rs b/src/parser/inline_babel_call.rs index a1ed4e36..1f17b96e 100644 --- a/src/parser/inline_babel_call.rs +++ b/src/parser/inline_babel_call.rs @@ -10,11 +10,13 @@ use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many_till; +use super::org_source::BracketDepth; use super::org_source::OrgSource; use super::Context; +use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::exiting::ExitClass; -use crate::parser::parser_context::BabelHeaderBracket; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; @@ -74,14 +76,11 @@ fn header<'r, 's>( ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("[")(input)?; - let parser_context = context - .with_additional_node(ContextElement::BabelHeaderBracket(BabelHeaderBracket { - position: remaining, - depth: 0, - })) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = header_end(remaining.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &header_end, + exit_matcher: &exit_with_depth, })); let (remaining, name) = recognize(many_till( @@ -92,28 +91,30 @@ fn header<'r, 's>( Ok((remaining, name)) } +fn header_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _header_end(context, input, starting_bracket_depth) + } +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn header_end<'r, 's>( - context: Context<'r, 's>, +fn _header_end<'r, 's>( + _context: Context<'r, 's>, input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, ) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside an inline babel call header."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '(' => { - current_depth += 1; - } - ')' if current_depth == 0 => { - panic!("Exceeded inline babel call header bracket depth.") - } - ')' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } + let current_depth = input.get_bracket_depth() - starting_bracket_depth; + if current_depth > 0 { + // Its impossible for the next character to end the header if we're any amount of bracket deep + return Err(nom::Err::Error(CustomError::MyError(MyError( + "NoHeaderEnd".into(), + )))); + } + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the header. + unreachable!("Exceeded header bracket depth.") } alt((tag("]"), line_ending))(input) } @@ -125,14 +126,11 @@ fn argument<'r, 's>( ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("(")(input)?; - let parser_context = context - .with_additional_node(ContextElement::BabelHeaderBracket(BabelHeaderBracket { - position: remaining, - depth: 0, - })) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = argument_end(remaining.get_parenthesis_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &argument_end, + exit_matcher: &exit_with_depth, })); let (remaining, name) = recognize(many_till( @@ -143,39 +141,30 @@ fn argument<'r, 's>( Ok((remaining, name)) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn argument_end<'r, 's>( - context: Context<'r, 's>, - input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside an inline babel call argument."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded inline babel call argument bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } +fn argument_end( + starting_parenthesis_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _argument_end(context, input, starting_parenthesis_depth) } - alt((tag(")"), line_ending))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r BabelHeaderBracket<'s>> { - for node in context.iter() { - match node.get_data() { - ContextElement::BabelHeaderBracket(depth) => return Some(depth), - _ => {} - } +fn _argument_end<'r, 's>( + _context: Context<'r, 's>, + input: OrgSource<'s>, + starting_parenthesis_depth: BracketDepth, +) -> Res, OrgSource<'s>> { + let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth; + if current_depth > 0 { + // Its impossible for the next character to end the argument if we're any amount of parenthesis deep + return Err(nom::Err::Error(CustomError::MyError(MyError( + "NoArgumentEnd".into(), + )))); } - None + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing parenthesis should end the argument. + unreachable!("Exceeded argument parenthesis depth.") + } + alt((tag(")"), line_ending))(input) } diff --git a/src/parser/inline_source_block.rs b/src/parser/inline_source_block.rs index f25be84e..bde53ee3 100644 --- a/src/parser/inline_source_block.rs +++ b/src/parser/inline_source_block.rs @@ -1,3 +1,4 @@ +use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; use nom::character::complete::anychar; @@ -11,14 +12,15 @@ use nom::multi::many_till; #[cfg(feature = "tracing")] use tracing::span; +use super::org_source::BracketDepth; use super::org_source::OrgSource; use super::Context; use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::exiting::ExitClass; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; -use crate::parser::parser_context::InlineSourceBlockBracket; use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; @@ -75,16 +77,11 @@ fn header<'r, 's>( ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("[")(input)?; - let parser_context = context - .with_additional_node(ContextElement::InlineSourceBlockBracket( - InlineSourceBlockBracket { - position: remaining, - depth: 0, - }, - )) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = header_end(remaining.get_bracket_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Beta, - exit_matcher: &header_end, + exit_matcher: &exit_with_depth, })); let (remaining, header_contents) = recognize(many_till( @@ -95,37 +92,32 @@ fn header<'r, 's>( Ok((remaining, header_contents)) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn header_end<'r, 's>( - context: Context<'r, 's>, - input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside an inline source block header."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '[' => { - current_depth += 1; - } - ']' if current_depth == 0 => { - panic!("Exceeded inline source block header bracket depth.") - } - ']' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } - } - if current_depth == 0 { - let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("]")(input); - if close_bracket.is_ok() { - return close_bracket; - } +fn header_end( + starting_bracket_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _header_end(context, input, starting_bracket_depth) } +} - line_ending(input) +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn _header_end<'r, 's>( + _context: Context<'r, 's>, + input: OrgSource<'s>, + starting_bracket_depth: BracketDepth, +) -> Res, OrgSource<'s>> { + let current_depth = input.get_bracket_depth() - starting_bracket_depth; + if current_depth > 0 { + // Its impossible for the next character to end the header if we're any amount of bracket deep + return Err(nom::Err::Error(CustomError::MyError(MyError( + "NoHeaderEnd".into(), + )))); + } + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing bracket should end the header. + unreachable!("Exceeded header bracket depth.") + } + alt((tag("]"), line_ending))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -135,16 +127,11 @@ fn body<'r, 's>( ) -> Res, OrgSource<'s>> { let (remaining, _) = tag("{")(input)?; - let parser_context = context - .with_additional_node(ContextElement::InlineSourceBlockBracket( - InlineSourceBlockBracket { - position: remaining, - depth: 0, - }, - )) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = body_end(remaining.get_brace_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Beta, - exit_matcher: &body_end, + exit_matcher: &exit_with_depth, })); let (remaining, body_contents) = recognize(many_till( @@ -165,60 +152,28 @@ fn body<'r, 's>( Ok((remaining, body_contents)) } +fn body_end( + starting_brace_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| _body_end(context, input, starting_brace_depth) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn body_end<'r, 's>( - context: Context<'r, 's>, +fn _body_end<'r, 's>( + _context: Context<'r, 's>, input: OrgSource<'s>, + starting_brace_depth: BracketDepth, ) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside an inline source block body."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '{' => { - current_depth += 1; - } - '}' if current_depth == 0 => { - panic!("Exceeded inline source block body bracket depth.") - } - '}' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } + let current_depth = input.get_brace_depth() - starting_brace_depth; + if current_depth > 0 { + // Its impossible for the next character to end the body if we're any amount of brace deep + return Err(nom::Err::Error(CustomError::MyError(MyError( + "NoBodyEnd".into(), + )))); } - { - #[cfg(feature = "tracing")] - let span = span!( - tracing::Level::DEBUG, - "inside end body", - remaining = Into::<&str>::into(input), - current_depth = current_depth - ); - #[cfg(feature = "tracing")] - let _enter = span.enter(); - - if current_depth == 0 { - let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("}")(input); - if close_bracket.is_ok() { - return close_bracket; - } - } + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing brace should end the body. + unreachable!("Exceeded body brace depth.") } - - line_ending(input) -} - -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn get_bracket_depth<'r, 's>( - context: Context<'r, 's>, -) -> Option<&'r InlineSourceBlockBracket<'s>> { - for node in context.iter() { - match node.get_data() { - ContextElement::InlineSourceBlockBracket(depth) => return Some(depth), - _ => {} - } - } - None + alt((tag("}"), line_ending))(input) } diff --git a/src/parser/latex_fragment.rs b/src/parser/latex_fragment.rs index 0fc704b7..e5b008ad 100644 --- a/src/parser/latex_fragment.rs +++ b/src/parser/latex_fragment.rs @@ -202,17 +202,13 @@ fn bordered_dollar_fragment<'r, 's>( // TODO: I'm assuming I should be peeking at the borders but the documentation is not clear. Test to figure out. let (_, _) = peek(parser_with_context!(open_border)(context))(remaining)?; - // TODO: As an optimization it would be nice to exit early upon hitting the 3rd line break - let (remaining, _) = verify( - recognize(many_till( - anychar, - peek(alt(( - parser_with_context!(exit_matcher_parser)(context), - tag("$"), - ))), - )), - |body: &OrgSource<'_>| Into::<&str>::into(body).lines().take(4).count() <= 3, - )(remaining)?; + let (remaining, _) = recognize(many_till( + anychar, + peek(alt(( + parser_with_context!(exit_matcher_parser)(context), + tag("$"), + ))), + ))(remaining)?; let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?; let (remaining, _) = tag("$")(remaining)?; diff --git a/src/parser/object.rs b/src/parser/object.rs index 8ad6b8ca..9064a6fc 100644 --- a/src/parser/object.rs +++ b/src/parser/object.rs @@ -374,3 +374,9 @@ impl<'s> Source<'s> for Timestamp<'s> { self.source } } + +impl<'s> Source<'s> for PlainText<'s> { + fn get_source(&'s self) -> &'s str { + self.source + } +} diff --git a/src/parser/object_parser.rs b/src/parser/object_parser.rs index b119d094..18faf11b 100644 --- a/src/parser/object_parser.rs +++ b/src/parser/object_parser.rs @@ -33,7 +33,7 @@ pub fn standard_set_object<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - alt(( + let (remaining, object) = alt(( map(parser_with_context!(timestamp)(context), Object::Timestamp), map(parser_with_context!(subscript)(context), Object::Subscript), map( @@ -82,7 +82,8 @@ pub fn standard_set_object<'r, 's>( map(parser_with_context!(angle_link)(context), Object::AngleLink), map(parser_with_context!(org_macro)(context), Object::OrgMacro), map(parser_with_context!(plain_text)(context), Object::PlainText), - ))(input) + ))(input)?; + Ok((remaining, object)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -90,7 +91,7 @@ pub fn minimal_set_object<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - alt(( + let (remaining, object) = alt(( map(parser_with_context!(subscript)(context), Object::Subscript), map( parser_with_context!(superscript)(context), @@ -103,7 +104,8 @@ pub fn minimal_set_object<'r, 's>( ), parser_with_context!(text_markup)(context), map(parser_with_context!(plain_text)(context), Object::PlainText), - ))(input) + ))(input)?; + Ok((remaining, object)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -111,7 +113,7 @@ pub fn any_object_except_plain_text<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - alt(( + let (remaining, object) = alt(( map(parser_with_context!(timestamp)(context), Object::Timestamp), map(parser_with_context!(subscript)(context), Object::Subscript), map( @@ -159,7 +161,8 @@ pub fn any_object_except_plain_text<'r, 's>( map(parser_with_context!(plain_link)(context), Object::PlainLink), map(parser_with_context!(angle_link)(context), Object::AngleLink), map(parser_with_context!(org_macro)(context), Object::OrgMacro), - ))(input) + ))(input)?; + Ok((remaining, object)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -168,7 +171,7 @@ pub fn regular_link_description_object_set<'r, 's>( input: OrgSource<'s>, ) -> Res, Object<'s>> { // TODO: It can also contain another link, but only when it is a plain or angle link. It can contain square brackets, but not ]] - alt(( + let (remaining, object) = alt(( map( parser_with_context!(export_snippet)(context), Object::ExportSnippet, @@ -187,7 +190,8 @@ pub fn regular_link_description_object_set<'r, 's>( ), map(parser_with_context!(org_macro)(context), Object::OrgMacro), parser_with_context!(minimal_set_object)(context), - ))(input) + ))(input)?; + Ok((remaining, object)) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -195,7 +199,7 @@ pub fn table_cell_set_object<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - alt(( + let (remaining, object) = alt(( map(parser_with_context!(citation)(context), Object::Citation), map( parser_with_context!(export_snippet)(context), @@ -220,5 +224,6 @@ pub fn table_cell_set_object<'r, 's>( map(parser_with_context!(target)(context), Object::Target), map(parser_with_context!(timestamp)(context), Object::Timestamp), parser_with_context!(minimal_set_object)(context), - ))(input) + ))(input)?; + Ok((remaining, object)) } diff --git a/src/parser/org_source.rs b/src/parser/org_source.rs index 9789c8ea..231cb105 100644 --- a/src/parser/org_source.rs +++ b/src/parser/org_source.rs @@ -11,15 +11,28 @@ use nom::Slice; use crate::error::CustomError; use crate::error::MyError; -#[derive(Debug, Copy, Clone)] +pub type BracketDepth = i16; + +#[derive(Copy, Clone)] pub struct OrgSource<'s> { full_source: &'s str, start: usize, end: usize, // exclusive start_of_line: usize, + bracket_depth: BracketDepth, // [] + brace_depth: BracketDepth, // {} + parenthesis_depth: BracketDepth, // () preceding_character: Option, } +impl<'s> std::fmt::Debug for OrgSource<'s> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("OrgSource") + .field(&Into::<&str>::into(self)) + .finish() + } +} + impl<'s> OrgSource<'s> { /// Returns a wrapped string that keeps track of values we need for parsing org-mode. /// @@ -31,6 +44,9 @@ impl<'s> OrgSource<'s> { end: input.len(), start_of_line: 0, preceding_character: None, + bracket_depth: 0, + brace_depth: 0, + parenthesis_depth: 0, } } @@ -56,6 +72,18 @@ impl<'s> OrgSource<'s> { assert!(other.end <= self.end); self.slice(..(other.start - self.start)) } + + pub fn get_bracket_depth(&self) -> BracketDepth { + self.bracket_depth + } + + pub fn get_brace_depth(&self) -> BracketDepth { + self.brace_depth + } + + pub fn get_parenthesis_depth(&self) -> BracketDepth { + self.parenthesis_depth + } } impl<'s> InputTake for OrgSource<'s> { @@ -119,10 +147,36 @@ where } let skipped_text = &self.full_source[self.start..new_start]; - let start_of_line = skipped_text - .rfind('\n') - .map(|idx| self.start + idx + 1) - .unwrap_or(self.start_of_line); + let mut start_of_line = self.start_of_line; + let mut bracket_depth = self.bracket_depth; + let mut brace_depth = self.brace_depth; + let mut parenthesis_depth = self.parenthesis_depth; + for (offset, byte) in skipped_text.bytes().enumerate() { + match byte { + b'\n' => { + start_of_line = self.start + offset + 1; + } + b'[' => { + bracket_depth += 1; + } + b']' => { + bracket_depth -= 1; + } + b'{' => { + brace_depth += 1; + } + b'}' => { + brace_depth -= 1; + } + b'(' => { + parenthesis_depth += 1; + } + b')' => { + parenthesis_depth -= 1; + } + _ => {} + }; + } OrgSource { full_source: self.full_source, @@ -130,6 +184,9 @@ where end: new_end, start_of_line, preceding_character: skipped_text.chars().last(), + bracket_depth, + brace_depth, + parenthesis_depth, } } } @@ -246,7 +303,9 @@ impl<'s> InputTakeAtPosition for OrgSource<'s> { } } -pub fn convert_error(err: nom::Err>>) -> nom::Err> { +pub fn convert_error<'a, I: Into>>( + err: nom::Err, +) -> nom::Err> { match err { nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed), nom::Err::Error(err) => nom::Err::Error(err.into()), @@ -369,4 +428,19 @@ mod tests { assert_eq!(input.get_preceding_character(), None); assert_eq!(input.slice(8..).get_preceding_character(), Some('💛')); } + + #[test] + fn depth() { + let input = OrgSource::new("[][()][({)]}}}}"); + assert_eq!(input.get_bracket_depth(), 0); + assert_eq!(input.get_brace_depth(), 0); + assert_eq!(input.get_parenthesis_depth(), 0); + assert_eq!(input.slice(4..).get_bracket_depth(), 1); + assert_eq!(input.slice(4..).get_brace_depth(), 0); + assert_eq!(input.slice(4..).get_parenthesis_depth(), 1); + assert_eq!(input.slice(4..).slice(6..).get_bracket_depth(), 1); + assert_eq!(input.slice(4..).slice(6..).get_brace_depth(), 1); + assert_eq!(input.slice(4..).slice(6..).get_parenthesis_depth(), 0); + assert_eq!(input.slice(14..).get_brace_depth(), -2); + } } diff --git a/src/parser/parser_context.rs b/src/parser/parser_context.rs index 83760aea..f4e72d55 100644 --- a/src/parser/parser_context.rs +++ b/src/parser/parser_context.rs @@ -110,10 +110,9 @@ impl<'r, 's> ContextTree<'r, 's> { pub enum ContextElement<'r, 's> { /// Stores a parser that indicates that children should exit upon matching an exit matcher. ExitMatcherNode(ExitMatcherNode<'r>), - Context(&'r str), - /// Stores the name of the greater block. - GreaterBlock(&'s str), + /// Stores the name of the current element to prevent directly nesting elements of the same type. + Context(&'r str), /// Indicates if elements should consume the whitespace after them. ConsumeTrailingWhitespace(bool), @@ -124,71 +123,6 @@ pub enum ContextElement<'r, 's> { /// 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>>), - - /// Stores the current bracket depth inside a footnote reference's definition. - /// - /// The definition inside a footnote reference must have balanced - /// brackets [] inside the definition, so this stores the amount - /// of opening brackets subtracted by the amount of closing - /// brackets within the definition must equal zero. - /// - /// A reference to the position in the string is also included so - /// unbalanced brackets can be detected in the middle of an - /// object. - FootnoteReferenceDefinition(FootnoteReferenceDefinition<'s>), - - /// Stores the current bracket depth inside a citation. - /// - /// The global prefix, global suffix, key prefix, and key suffix - /// inside a footnote reference must have balanced brackets [] - /// inside the definition, so this stores the amount of opening - /// brackets subtracted by the amount of closing brackets within - /// the definition must equal zero. None of the prefixes or - /// suffixes can be nested inside each other so we can use a - /// single type for this without conflict. - /// - /// A reference to the position in the string is also included so - /// unbalanced brackets can be detected in the middle of an - /// object. - CitationBracket(CitationBracket<'s>), - - /// Stores the current bracket or parenthesis depth inside an inline babel call. - /// - /// Inside an inline babel call the headers must have balanced - /// parentheses () and the arguments must have balanced brackets - /// [], so this stores the amount of opening brackets subtracted - /// by the amount of closing brackets within the definition must - /// equal zero. - /// - /// A reference to the position in the string is also included so - /// unbalanced brackets can be detected in the middle of an - /// object. - BabelHeaderBracket(BabelHeaderBracket<'s>), - - /// Stores the current bracket or parenthesis depth inside an inline babel call. - /// - /// Inside an inline babel call the headers must have balanced - /// parentheses () and the arguments must have balanced brackets - /// [], so this stores the amount of opening brackets subtracted - /// by the amount of closing brackets within the definition must - /// equal zero. - /// - /// A reference to the position in the string is also included so - /// unbalanced brackets can be detected in the middle of an - /// object. - InlineSourceBlockBracket(InlineSourceBlockBracket<'s>), - - /// Stores the current bracket or parenthesis depth inside a - /// superscript or superscript. - /// - /// Inside the braces of a subscript or superscript there must be - /// balanced braces {}, so this stores the amount of opening - /// braces subtracted by the amount of closing braces within the - /// definition must equal zero. - /// - /// A reference to the position in the string is also included so - /// unbalanced braces can be detected in the middle of an object. - SubscriptSuperscriptBrace(SubscriptSuperscriptBrace<'s>), } pub struct ExitMatcherNode<'r> { @@ -196,36 +130,6 @@ pub struct ExitMatcherNode<'r> { pub class: ExitClass, } -#[derive(Debug)] -pub struct FootnoteReferenceDefinition<'s> { - pub position: OrgSource<'s>, - pub depth: usize, -} - -#[derive(Debug)] -pub struct CitationBracket<'s> { - pub position: OrgSource<'s>, - pub depth: usize, -} - -#[derive(Debug)] -pub struct BabelHeaderBracket<'s> { - pub position: OrgSource<'s>, - pub depth: usize, -} - -#[derive(Debug)] -pub struct InlineSourceBlockBracket<'s> { - pub position: OrgSource<'s>, - pub depth: usize, -} - -#[derive(Debug)] -pub struct SubscriptSuperscriptBrace<'s> { - pub position: OrgSource<'s>, - pub depth: usize, -} - impl<'r> std::fmt::Debug for ExitMatcherNode<'r> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ExitMatcherNode"); diff --git a/src/parser/subscript_and_superscript.rs b/src/parser/subscript_and_superscript.rs index 43978da3..52d518a7 100644 --- a/src/parser/subscript_and_superscript.rs +++ b/src/parser/subscript_and_superscript.rs @@ -11,6 +11,7 @@ use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many_till; +use super::org_source::BracketDepth; use super::org_source::OrgSource; use super::Context; use super::Object; @@ -21,7 +22,6 @@ use crate::parser::exiting::ExitClass; use crate::parser::object_parser::standard_set_object; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; -use crate::parser::parser_context::SubscriptSuperscriptBrace; use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; @@ -154,16 +154,11 @@ fn script_with_braces<'r, 's>( input: OrgSource<'s>, ) -> Res, Vec>> { let (remaining, _) = tag("{")(input)?; - let parser_context = context - .with_additional_node(ContextElement::SubscriptSuperscriptBrace( - SubscriptSuperscriptBrace { - position: remaining.into(), - depth: 0, - }, - )) - .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { + let exit_with_depth = script_with_braces_end(remaining.get_brace_depth()); + let parser_context = + context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Gamma, - exit_matcher: &script_with_braces_end, + exit_matcher: &exit_with_depth, })); let (remaining, (children, _exit_contents)) = many_till( @@ -175,49 +170,30 @@ fn script_with_braces<'r, 's>( Ok((remaining, children)) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn script_with_braces_end<'r, 's>( - context: Context<'r, 's>, - input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { - let context_depth = get_bracket_depth(context) - .expect("This function should only be called from inside a subscript or superscript."); - let text_since_context_entry = get_consumed(context_depth.position, input); - let mut current_depth = context_depth.depth; - for c in Into::<&str>::into(text_since_context_entry).chars() { - match c { - '{' => { - current_depth += 1; - } - '}' if current_depth == 0 => { - panic!("Exceeded subscript or superscript brace depth.") - } - '}' if current_depth > 0 => { - current_depth -= 1; - } - _ => {} - } +fn script_with_braces_end( + starting_brace_depth: BracketDepth, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| { + _script_with_braces_end(context, input, starting_brace_depth) } - if current_depth == 0 { - let close_bracket = tag::<&str, OrgSource<'_>, CustomError>>("}")(input); - if close_bracket.is_ok() { - return close_bracket; - } - } - return Err(nom::Err::Error(CustomError::MyError(MyError( - "Not a valid end for subscript or superscript.".into(), - )))); } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn get_bracket_depth<'r, 's>( - context: Context<'r, 's>, -) -> Option<&'r SubscriptSuperscriptBrace<'s>> { - for node in context.iter() { - match node.get_data() { - ContextElement::SubscriptSuperscriptBrace(depth) => return Some(depth), - _ => {} - } +fn _script_with_braces_end<'r, 's>( + _context: Context<'r, 's>, + input: OrgSource<'s>, + starting_brace_depth: BracketDepth, +) -> Res, OrgSource<'s>> { + let current_depth = input.get_brace_depth() - starting_brace_depth; + if current_depth > 0 { + // Its impossible for the next character to end the subscript or superscript if we're any amount of braces deep + return Err(nom::Err::Error(CustomError::MyError(MyError( + "Not a valid end for subscript or superscript.".into(), + )))); } - None + if current_depth < 0 { + // This shouldn't be possible because if depth is 0 then a closing brace should end the subscript or superscript. + unreachable!("Exceeded subscript or superscript brace depth.") + } + tag("}")(input) } diff --git a/src/parser/util.rs b/src/parser/util.rs index 12f3f25b..484a41dd 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -155,3 +155,16 @@ pub fn not_yet_implemented() -> Res, ()> { "Not implemented yet.".into(), )))); } + +#[allow(dead_code)] +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +/// Text from the current point until the next line break or end of file +/// +/// Useful for debugging. +pub fn text_until_eol<'r, 's>( + input: OrgSource<'s>, +) -> Result<&'s str, nom::Err>>> { + let line = recognize(many_till(anychar, alt((line_ending, eof))))(input) + .map(|(_remaining, line)| Into::<&str>::into(line))?; + Ok(line.trim()) +} diff --git a/tests/test_template b/tests/test_template index 442db660..a8b80318 100644 --- a/tests/test_template +++ b/tests/test_template @@ -11,7 +11,7 @@ fn {name}() {{ let diff_result = compare_document(&parsed_sexp, &rust_parsed).expect("Compare parsed documents."); diff_result - .print() + .print(org_contents.as_str()) .expect("Print document parse tree diff."); assert!(!diff_result.is_bad()); assert_eq!(remaining, "");