Compare commits

...

24 Commits

Author SHA1 Message Date
Tom Alexander
f592b73ae7
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
2023-08-29 15:17:16 -04:00
Tom Alexander
3206027b96
Add all entities. 2023-08-29 15:16:22 -04:00
Tom Alexander
3e6df7ba78
Print character offset from rust's parse perspective during compare. 2023-08-29 14:40:58 -04:00
Tom Alexander
ac313d093e
Improve error handling in compare. 2023-08-29 14:20:53 -04:00
Tom Alexander
f376f1cf8e
Add a test for empty sections. 2023-08-29 14:10:26 -04:00
Tom Alexander
f21385a901
Add a helper function for logging during debugging.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-29 14:03:14 -04:00
Tom Alexander
1d06d95bb1
Add a minimum stars figure to heading parser to reduce re-parses. 2023-08-29 14:01:28 -04:00
Tom Alexander
bfc88c1d1b
Use a detect_headline function instead of the full headline parse for the section_end exit matcher.
This shaved 2 seconds off the first 800 lines of org-mode/doc/org-guide.org.
2023-08-29 11:35:54 -04:00
Tom Alexander
f29720e5b9
Switch to using a type for bracket depth.
This is to make changing the type easier in the future.
2023-08-29 11:18:15 -04:00
Tom Alexander
27a9b5aeb1
Switch to i16 for backet depth count.
This is having a measurable performance increase. 32k bracket depth should be enough for any non-malicious document.
2023-08-29 11:14:50 -04:00
Tom Alexander
8051c3d2b7
Remove line number tracking.
The documentation was incorrect, none of the org-mode elements have a line number restriction for their contents.
2023-08-29 11:09:28 -04:00
Tom Alexander
bd97d2f69d
Switch to i32 for tracking bracket depth. 2023-08-29 11:07:00 -04:00
Tom Alexander
14b1d0526c
Manually implement Debug and make convert_error more generic.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-29 10:58:05 -04:00
Tom Alexander
288350daef
Iterate over the bytes instead of characters when counting brackets.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-28 03:52:21 -04:00
Tom Alexander
c683516620
Switch inline source blocks to using bracket depth from OrgSource instead of from the context.
This is for the same reasons as footnote references.
2023-08-28 03:04:33 -04:00
Tom Alexander
e731e8ff6b
Switch inline babel call to using bracket depth from OrgSource instead of from the context.
This is for the same reasons as footnote references.
2023-08-28 03:04:33 -04:00
Tom Alexander
4c2037ec44
Switch subscript and superscript to using bracket depth from OrgSource instead of from the context.
This is for the same reasons as footnote references.
2023-08-28 03:04:33 -04:00
Tom Alexander
a46b358549
Switch citations to using bracket depth from OrgSource instead of from the context.
This is for the same reasons as footnote references.
2023-08-28 03:04:32 -04:00
Tom Alexander
ec813e3b3f
Switch to using bracket depth from OrgSource instead of from the context for footnote references.
It is currently unknown if this will produce a performance increase, but unless it has a significant performance penalty we are going to go forward with this change because it makes it more explicit which values need to be read deeply from other elements (therefore needing to be in the context) vs values that can be bound to the exit matcher since they are only used within the confines of the current element.

I suspect we will get a performance boost since it will be reducing the nodes that need to be walked in the context but maintaining bracket depth count over the entire document instead of only inside elements that need balanced brackets could cost us.
2023-08-28 03:04:32 -04:00
Tom Alexander
f11f7bcc73
Keep track of bracket, brace, and parenthesis depth when iterating over the OrgSource. 2023-08-28 01:18:46 -04:00
Tom Alexander
9e0e5f6f0a
Remove line number limit for LaTeX fragments. 2023-08-28 01:18:46 -04:00
Tom Alexander
16e788c36c
Add tests for LaTeX fragments and text markup that span more than three lines.
The documentation currently states that the body for these cannot span more than three lines but that is not the behavior I am seeing from emacs in practice. Waiting on a mailing list response to tell me if this is a documentation error or a parser error.
2023-08-28 01:18:46 -04:00
Tom Alexander
1952d175c0
Record line number in OrgSource.
This will be used for elements who have limits on the number of lines inside of them. This includes LaTeX fragment bodies and text markup.
2023-08-28 01:18:45 -04:00
Tom Alexander
20c17c40be
Switch greater blocks to using name provided when building exit matcher instead of from context. 2023-08-28 01:18:45 -04:00
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))
}
#[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>, 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;
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)
}
'}' 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;
}
}
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>, 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(),
))));
}
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, "");