Started switching over to a stack-based context tree with global settings.
This change should hopefully allow for matchers to have captured borrowed values, it should eliminate the use of heap-allocated reference counting on the context nodes, and it adds in a global settings struct for passing around values that do not change during parsing.
This commit is contained in:
parent
acf1205e75
commit
66d10a7a1b
@ -1,183 +1,66 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct List<T> {
|
|
||||||
head: Option<Rc<Node<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Clone for List<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
List {
|
|
||||||
head: self.head.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Node<T> {
|
pub struct List<'parent, T> {
|
||||||
data: T,
|
data: T,
|
||||||
parent: Option<Rc<Node<T>>>,
|
parent: Link<'parent, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Link<'parent, T> = Option<&'parent List<'parent, T>>;
|
||||||
|
|
||||||
|
impl<'parent, T> List<'parent, T> {
|
||||||
|
pub fn new(first_item: T) -> Self {
|
||||||
|
Self {
|
||||||
|
data: first_item,
|
||||||
|
parent: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Node<T> {
|
|
||||||
pub fn get_data(&self) -> &T {
|
pub fn get_data(&self) -> &T {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_parent(&'parent self) -> Link<'parent, T> {
|
||||||
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> List<T> {
|
pub fn iter(&self) -> Iter<'_, T> {
|
||||||
pub fn new() -> Self {
|
Iter { next: Some(self) }
|
||||||
List { head: None }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn branch_from(trunk: &Rc<Node<T>>) -> Self {
|
pub trait ListType<'parent, T> {
|
||||||
|
fn push(&'parent self, item: T) -> List<'parent, T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'parent, T> ListType<'parent, T> for List<'parent, T> {
|
||||||
|
fn push(&'parent self, item: T) -> Self {
|
||||||
|
Self {
|
||||||
|
data: item,
|
||||||
|
parent: Some(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'parent, T> ListType<'parent, T> for Link<'parent, T> {
|
||||||
|
fn push(&'parent self, item: T) -> List<'parent, T> {
|
||||||
List {
|
List {
|
||||||
head: Some(trunk.clone()),
|
data: item,
|
||||||
}
|
parent: *self,
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_front(&self, data: T) -> List<T> {
|
|
||||||
List {
|
|
||||||
head: Some(Rc::new(Node {
|
|
||||||
data: data,
|
|
||||||
parent: self.head.clone(),
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_front(&mut self) -> (Option<T>, List<T>) {
|
|
||||||
match self.head.take() {
|
|
||||||
None => (None, List::new()),
|
|
||||||
Some(popped_node) => {
|
|
||||||
let extracted_node = match Rc::try_unwrap(popped_node) {
|
|
||||||
Ok(node) => node,
|
|
||||||
Err(_) => panic!("try_unwrap failed on Rc in pop_front on List."),
|
|
||||||
};
|
|
||||||
(
|
|
||||||
Some(extracted_node.data),
|
|
||||||
List {
|
|
||||||
head: extracted_node.parent,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub struct Iter<'a, T> {
|
||||||
pub fn without_front(&self) -> List<T> {
|
next: Link<'a, T>,
|
||||||
List {
|
|
||||||
head: self.head.as_ref().map(|node| node.parent.clone()).flatten(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
impl<'a, T> Iterator for Iter<'a, T> {
|
||||||
pub fn get_data(&self) -> Option<&T> {
|
type Item = &'a T;
|
||||||
self.head.as_ref().map(|rc_node| &rc_node.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.head.is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr_eq(&self, other: &List<T>) -> bool {
|
|
||||||
match (self.head.as_ref(), other.head.as_ref()) {
|
|
||||||
(None, None) => true,
|
|
||||||
(None, Some(_)) | (Some(_), None) => false,
|
|
||||||
(Some(me), Some(them)) => Rc::ptr_eq(me, them),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &Rc<Node<T>>> {
|
|
||||||
NodeIter {
|
|
||||||
position: &self.head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn iter_until<'a>(&'a self, other: &'a List<T>) -> impl Iterator<Item = &Rc<Node<T>>> {
|
|
||||||
NodeIterUntil {
|
|
||||||
position: &self.head,
|
|
||||||
stop: &other.head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn into_iter_until<'a>(self, other: &'a List<T>) -> impl Iterator<Item = T> + 'a {
|
|
||||||
NodeIntoIterUntil {
|
|
||||||
position: self,
|
|
||||||
stop: &other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NodeIter<'a, T> {
|
|
||||||
position: &'a Option<Rc<Node<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for NodeIter<'a, T> {
|
|
||||||
type Item = &'a Rc<Node<T>>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let (return_value, next_position) = match &self.position {
|
let ret = self.next.map(|link| link.get_data());
|
||||||
None => return None,
|
self.next = self.next.map(|link| link.get_parent()).flatten();
|
||||||
Some(rc_node) => {
|
ret
|
||||||
let next_position = &rc_node.parent;
|
|
||||||
let return_value = rc_node;
|
|
||||||
(return_value, next_position)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.position = next_position;
|
|
||||||
Some(return_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NodeIterUntil<'a, T> {
|
|
||||||
position: &'a Option<Rc<Node<T>>>,
|
|
||||||
stop: &'a Option<Rc<Node<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for NodeIterUntil<'a, T> {
|
|
||||||
type Item = &'a Rc<Node<T>>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match (self.position, self.stop) {
|
|
||||||
(_, None) => {}
|
|
||||||
(None, _) => {}
|
|
||||||
(Some(this_rc), Some(stop_rc)) => {
|
|
||||||
if Rc::ptr_eq(this_rc, stop_rc) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (return_value, next_position) = match &self.position {
|
|
||||||
None => return None,
|
|
||||||
Some(rc_node) => {
|
|
||||||
let next_position = &rc_node.parent;
|
|
||||||
let return_value = rc_node;
|
|
||||||
(return_value, next_position)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.position = next_position;
|
|
||||||
Some(return_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NodeIntoIterUntil<'a, T> {
|
|
||||||
position: List<T>,
|
|
||||||
stop: &'a List<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for NodeIntoIterUntil<'a, T> {
|
|
||||||
type Item = T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.position.ptr_eq(self.stop) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let (popped_element, new_position) = self.position.pop_front();
|
|
||||||
self.position = new_position;
|
|
||||||
popped_element
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,4 +110,14 @@ pub use object::Timestamp;
|
|||||||
pub use object::Underline;
|
pub use object::Underline;
|
||||||
pub use object::Verbatim;
|
pub use object::Verbatim;
|
||||||
pub use source::Source;
|
pub use source::Source;
|
||||||
type Context<'r, 's> = &'r parser_context::ContextTree<'r, 's>;
|
|
||||||
|
use self::org_source::OrgSource;
|
||||||
|
use self::parser_context::Context;
|
||||||
|
use crate::error::Res;
|
||||||
|
|
||||||
|
type RefContext<'r, 's> = &'r Context<'r, 's>;
|
||||||
|
trait ContextMatcher =
|
||||||
|
for<'r, 's> Fn(RefContext<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>;
|
||||||
|
type DynContextMatcher<'c> = dyn ContextMatcher + 'c;
|
||||||
|
trait Matcher = for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>;
|
||||||
|
type DynMatcher<'c> = dyn Matcher + 'c;
|
||||||
|
@ -1,110 +1,96 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use nom::combinator::eof;
|
|
||||||
use nom::IResult;
|
|
||||||
|
|
||||||
use super::list::List;
|
use super::list::List;
|
||||||
use super::list::Node;
|
use super::DynContextMatcher;
|
||||||
use super::org_source::OrgSource;
|
|
||||||
use super::Context;
|
|
||||||
use super::Object;
|
use super::Object;
|
||||||
use crate::error::CustomError;
|
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
|
use crate::parser::list::ListType;
|
||||||
|
|
||||||
type Matcher =
|
// type Matcher =
|
||||||
dyn for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>;
|
// dyn for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
// #[derive(Debug, Clone)]
|
||||||
pub struct ContextTree<'r, 's> {
|
// pub struct ContextTree<'r, 's> {
|
||||||
tree: List<ContextElement<'r, 's>>,
|
// tree: List<ContextElement<'r, 's>>,
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'r, 's> ContextTree<'r, 's> {
|
// impl<'r, 's> ContextTree<'r, 's> {
|
||||||
pub fn new() -> Self {
|
// pub fn new() -> Self {
|
||||||
ContextTree { tree: List::new() }
|
// ContextTree { tree: List::new() }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn branch_from(trunk: &Rc<Node<ContextElement<'r, 's>>>) -> Self {
|
// pub fn branch_from(trunk: &Rc<Node<ContextElement<'r, 's>>>) -> Self {
|
||||||
ContextTree {
|
// ContextTree {
|
||||||
tree: List::branch_from(trunk),
|
// tree: List::branch_from(trunk),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
pub fn ptr_eq<'x, 'y>(&self, other: &ContextTree<'x, 'y>) -> bool {
|
// pub fn ptr_eq<'x, 'y>(&self, other: &ContextTree<'x, 'y>) -> bool {
|
||||||
self.tree.ptr_eq(&other.tree)
|
// self.tree.ptr_eq(&other.tree)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn with_additional_node(&self, data: ContextElement<'r, 's>) -> ContextTree<'r, 's> {
|
// pub fn with_additional_node(&self, data: ContextElement<'r, 's>) -> ContextTree<'r, 's> {
|
||||||
let new_list = self.tree.push_front(data);
|
// let new_list = self.tree.push_front(data);
|
||||||
ContextTree { tree: new_list }
|
// ContextTree { tree: new_list }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &Rc<Node<ContextElement<'r, 's>>>> {
|
// pub fn iter(&self) -> impl Iterator<Item = &Rc<Node<ContextElement<'r, 's>>>> {
|
||||||
self.tree.iter()
|
// self.tree.iter()
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
// #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn check_exit_matcher(
|
// pub fn check_exit_matcher(
|
||||||
&'r self,
|
// &'r self,
|
||||||
i: OrgSource<'s>,
|
// i: OrgSource<'s>,
|
||||||
) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError<OrgSource<'s>>> {
|
// ) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError<OrgSource<'s>>> {
|
||||||
// Special check for EOF. We don't just make this a document-level exit matcher since the IgnoreParent ChainBehavior could cause early exit matchers to not run.
|
// // Special check for EOF. We don't just make this a document-level exit matcher since the IgnoreParent ChainBehavior could cause early exit matchers to not run.
|
||||||
let at_end_of_file = eof(i);
|
// let at_end_of_file = eof(i);
|
||||||
if at_end_of_file.is_ok() {
|
// if at_end_of_file.is_ok() {
|
||||||
return at_end_of_file;
|
// return at_end_of_file;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// let blocked_context =
|
// let mut current_class_filter = ExitClass::Gamma;
|
||||||
// self.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
// for current_node in self.iter() {
|
||||||
// exit_matcher: ChainBehavior::IgnoreParent(Some(&always_fail)),
|
// let context_element = current_node.get_data();
|
||||||
// }));
|
// match context_element {
|
||||||
|
// ContextElement::ExitMatcherNode(exit_matcher) => {
|
||||||
|
// if exit_matcher.class as u32 <= current_class_filter as u32 {
|
||||||
|
// current_class_filter = exit_matcher.class;
|
||||||
|
// let local_context = ContextTree::branch_from(current_node);
|
||||||
|
// let local_result = (exit_matcher.exit_matcher)(&local_context, i);
|
||||||
|
// if local_result.is_ok() {
|
||||||
|
// return local_result;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// _ => {}
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// // TODO: Make this a specific error instead of just a generic MyError
|
||||||
|
// return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
// "NoExit".into(),
|
||||||
|
// ))));
|
||||||
|
// }
|
||||||
|
|
||||||
let mut current_class_filter = ExitClass::Gamma;
|
// /// Indicates if elements should consume the whitespace after them.
|
||||||
for current_node in self.iter() {
|
// ///
|
||||||
let context_element = current_node.get_data();
|
// /// Defaults to true.
|
||||||
match context_element {
|
// pub fn should_consume_trailing_whitespace(&self) -> bool {
|
||||||
ContextElement::ExitMatcherNode(exit_matcher) => {
|
// self._should_consume_trailing_whitespace().unwrap_or(true)
|
||||||
if exit_matcher.class as u32 <= current_class_filter as u32 {
|
// }
|
||||||
current_class_filter = exit_matcher.class;
|
|
||||||
let local_context = ContextTree::branch_from(current_node);
|
|
||||||
let local_result = (exit_matcher.exit_matcher)(&local_context, i);
|
|
||||||
if local_result.is_ok() {
|
|
||||||
return local_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// TODO: Make this a specific error instead of just a generic MyError
|
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"NoExit".into(),
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Indicates if elements should consume the whitespace after them.
|
// fn _should_consume_trailing_whitespace(&self) -> Option<bool> {
|
||||||
///
|
// for current_node in self.iter() {
|
||||||
/// Defaults to true.
|
// let context_element = current_node.get_data();
|
||||||
pub fn should_consume_trailing_whitespace(&self) -> bool {
|
// match context_element {
|
||||||
self._should_consume_trailing_whitespace().unwrap_or(true)
|
// ContextElement::ConsumeTrailingWhitespace(should) => {
|
||||||
}
|
// return Some(*should);
|
||||||
|
// }
|
||||||
fn _should_consume_trailing_whitespace(&self) -> Option<bool> {
|
// _ => {}
|
||||||
for current_node in self.iter() {
|
// }
|
||||||
let context_element = current_node.get_data();
|
// }
|
||||||
match context_element {
|
// None
|
||||||
ContextElement::ConsumeTrailingWhitespace(should) => {
|
// }
|
||||||
return Some(*should);
|
// }
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ContextElement<'r, 's> {
|
pub enum ContextElement<'r, 's> {
|
||||||
@ -126,7 +112,8 @@ pub enum ContextElement<'r, 's> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExitMatcherNode<'r> {
|
pub struct ExitMatcherNode<'r> {
|
||||||
pub exit_matcher: &'r Matcher,
|
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
|
||||||
|
pub exit_matcher: &'r DynContextMatcher<'r>,
|
||||||
pub class: ExitClass,
|
pub class: ExitClass,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,3 +124,38 @@ impl<'r> std::fmt::Debug for ExitMatcherNode<'r> {
|
|||||||
formatter.finish()
|
formatter.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GlobalSettings<'s> {
|
||||||
|
placeholder: Option<&'s str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> GlobalSettings<'s> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
GlobalSettings { placeholder: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Context<'r, 's> {
|
||||||
|
global_settings: &'s GlobalSettings<'s>,
|
||||||
|
tree: List<'r, ContextElement<'r, 's>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, 's> Context<'r, 's> {
|
||||||
|
pub fn new(
|
||||||
|
global_settings: &'s GlobalSettings<'s>,
|
||||||
|
tree: List<'r, ContextElement<'r, 's>>,
|
||||||
|
) -> Self {
|
||||||
|
Context {
|
||||||
|
global_settings,
|
||||||
|
tree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&self, data: ContextElement<'r, 's>) -> Self {
|
||||||
|
let new_tree = self.tree.push(data);
|
||||||
|
Self {
|
||||||
|
global_settings: self.global_settings,
|
||||||
|
tree: new_tree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user