Merge branch 'reduce_context_usage_in_exit_matchers'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded

This commit is contained in:
Tom Alexander 2023-08-29 15:17:16 -04:00
commit f592b73ae7
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
26 changed files with 1135 additions and 637 deletions

View File

@ -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,
}
}

1
elisp_snippets/README.md Normal file
View File

@ -0,0 +1 @@
This folder is for snippets of elisp that are useful for development.

View File

@ -0,0 +1,5 @@
(dolist (var org-entities)
(when (listp var)
(message "\"%s\"," (nth 0 var))
)
)

View File

@ -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]

View File

@ -0,0 +1,2 @@
[fn:2:This footnote [ has balanced ] brackets inside it]
[fn::This footnote does not have balanced [ brackets inside it]

View File

@ -0,0 +1,6 @@
$foo
bar
baz
lorem
ipsum
dolar$

View File

@ -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

View File

@ -0,0 +1,9 @@
* Foo
* Bar
* Baz

View File

@ -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<String>,
children: Vec<DiffResult>,
children: Vec<DiffResult<'s>>,
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<dyn std::error::Error>> {
self.print_indented(0)
impl<'s> DiffResult<'s> {
pub fn print(&self, original_document: &str) -> Result<(), Box<dyn std::error::Error>> {
self.print_indented(0, original_document)
}
fn print_indented(&self, indentation: usize) -> Result<(), Box<dyn std::error::Error>> {
fn print_indented(
&self,
indentation: usize,
original_document: &str,
) -> Result<(), Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
match rust {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
match rust {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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<DiffResult, Box<dyn std::error::Error>> {
) -> Result<DiffResult<'s>, Box<dyn std::error::Error>> {
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,
})
}

View File

@ -55,20 +55,21 @@ fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
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.")?;

View File

@ -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<OrgSource<'s>, Vec<Object<'s>>> {
// 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>, 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>, 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<OrgSource<'_>>>("]")(input);
@ -144,15 +138,11 @@ fn global_suffix<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// 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>, 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>, 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<OrgSource<'_>>>("]")(input);

View File

@ -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<OrgSource<'s>, 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<OrgSource<'s>, Vec<Object<'s>>> {
// 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<OrgSource<'s>, Vec<Object<'s>>> {
// 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>, 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>, 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<OrgSource<'_>>>("]")(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>, 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>, 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<OrgSource<'_>>>("]")(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<OrgSource<'s>, O>
where
F: FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, 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))
}
}

View File

@ -139,7 +139,7 @@ fn _document<'r, 's>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, 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>, 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<OrgSource<'s>, 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<OrgSource<'s>, 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<OrgSource<'s>, ()> {
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),

View File

@ -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<OrgSource<'s>, 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>, 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<OrgSource<'s>, ()> {
fn entity_end<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?;
Ok((remaining, ()))

View File

@ -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<OrgSource<'s>, 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>(
))
}
fn footnote_definition_end(
starting_bracket_depth: BracketDepth,
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, 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>,
fn _footnote_definition_end<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
starting_bracket_depth: BracketDepth,
) -> Res<OrgSource<'s>, 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;
}
_ => {}
}
}
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
}

View File

@ -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>, 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>, 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>, 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
}

View File

@ -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>, 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>, 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>, 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>, 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>, 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;
fn argument_end(
starting_parenthesis_depth: BracketDepth,
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
move |context: Context, input: OrgSource<'_>| {
_argument_end(context, input, starting_parenthesis_depth)
}
']' if current_depth == 0 => {
panic!("Exceeded inline babel call argument bracket depth.")
}
']' if current_depth > 0 => {
current_depth -= 1;
}
_ => {}
}
}
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>, 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(),
))));
}
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.")
}
None
alt((tag(")"), line_ending))(input)
}

View File

@ -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>, 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>, 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<OrgSource<'_>>>("]")(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>, 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>, 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>, 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>, 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>, 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;
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(),
))));
}
'}' if current_depth == 0 => {
panic!("Exceeded inline source block body bracket depth.")
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.")
}
'}' if current_depth > 0 => {
current_depth -= 1;
}
_ => {}
}
}
{
#[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<OrgSource<'_>>>("}")(input);
if close_bracket.is_ok() {
return close_bracket;
}
}
}
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)
}

View File

@ -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(
let (remaining, _) = 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)?;
))(remaining)?;
let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?;
let (remaining, _) = tag("$")(remaining)?;

View File

@ -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
}
}

View File

@ -33,7 +33,7 @@ pub fn standard_set_object<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, 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<OrgSource<'s>, 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<OrgSource<'s>, 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<OrgSource<'s>, 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<OrgSource<'s>, 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))
}

View File

@ -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<char>,
}
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<CustomError<OrgSource<'_>>>) -> nom::Err<CustomError<&str>> {
pub fn convert_error<'a, I: Into<CustomError<&'a str>>>(
err: nom::Err<I>,
) -> nom::Err<CustomError<&'a str>> {
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);
}
}

View File

@ -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<Object<'s>>>),
/// 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");

View File

@ -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<OrgSource<'s>, Vec<Object<'s>>> {
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))
}
fn script_with_braces_end(
starting_brace_depth: BracketDepth,
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
move |context: Context, input: OrgSource<'_>| {
_script_with_braces_end(context, input, starting_brace_depth)
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_with_braces_end<'r, 's>(
context: Context<'r, 's>,
fn _script_with_braces_end<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
starting_brace_depth: BracketDepth,
) -> Res<OrgSource<'s>, 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;
}
_ => {}
}
}
if current_depth == 0 {
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("}")(input);
if close_bracket.is_ok() {
return close_bracket;
}
}
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(),
))));
}
#[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),
_ => {}
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.")
}
}
None
tag("}")(input)
}

View File

@ -155,3 +155,16 @@ pub fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
"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<CustomError<OrgSource<'s>>>> {
let line = recognize(many_till(anychar, alt((line_ending, eof))))(input)
.map(|(_remaining, line)| Into::<&str>::into(line))?;
Ok(line.trim())
}

View File

@ -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, "");