Starting to separate the context and parsed tokens into their own modules.
This commit is contained in:
20
src/context/exiting.rs
Normal file
20
src/context/exiting.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ExitClass {
|
||||
/// Headlines and sections.
|
||||
Document = 1,
|
||||
|
||||
/// Elements who take priority over beta elements when matching.
|
||||
Alpha = 20,
|
||||
|
||||
/// Elements who cede priority to alpha elements when matching.
|
||||
Beta = 300,
|
||||
|
||||
/// Elements who cede priority to alpha and beta elements when matching.
|
||||
Gamma = 4000,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ExitClass {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
66
src/context/list.rs
Normal file
66
src/context/list.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct List<'parent, T> {
|
||||
data: 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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub fn get_parent(&'parent self) -> Link<'parent, T> {
|
||||
self.parent
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
Iter { next: Some(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 {
|
||||
data: item,
|
||||
parent: *self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a, T> {
|
||||
next: Link<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let ret = self.next.map(|link| link.get_data());
|
||||
self.next = self.next.map(|link| link.get_parent()).flatten();
|
||||
ret
|
||||
}
|
||||
}
|
||||
11
src/context/mod.rs
Normal file
11
src/context/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
mod exiting;
|
||||
mod list;
|
||||
mod parser_context;
|
||||
mod parser_with_context;
|
||||
|
||||
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;
|
||||
203
src/context/parser_context.rs
Normal file
203
src/context/parser_context.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
use nom::combinator::eof;
|
||||
|
||||
use super::list::List;
|
||||
use super::org_source::OrgSource;
|
||||
use super::DynContextMatcher;
|
||||
use super::Object;
|
||||
use crate::error::Res;
|
||||
use crate::parser::exiting::ExitClass;
|
||||
use crate::parser::list::ListType;
|
||||
|
||||
// type Matcher =
|
||||
// dyn for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>;
|
||||
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct ContextTree<'r, 's> {
|
||||
// tree: List<ContextElement<'r, 's>>,
|
||||
// }
|
||||
|
||||
// impl<'r, 's> ContextTree<'r, 's> {
|
||||
// pub fn new() -> Self {
|
||||
// ContextTree { tree: List::new() }
|
||||
// }
|
||||
|
||||
// pub fn branch_from(trunk: &Rc<Node<ContextElement<'r, 's>>>) -> Self {
|
||||
// ContextTree {
|
||||
// tree: List::branch_from(trunk),
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[allow(dead_code)]
|
||||
// pub fn ptr_eq<'x, 'y>(&self, other: &ContextTree<'x, 'y>) -> bool {
|
||||
// self.tree.ptr_eq(&other.tree)
|
||||
// }
|
||||
|
||||
// pub fn with_additional_node(&self, data: ContextElement<'r, 's>) -> ContextTree<'r, 's> {
|
||||
// let new_list = self.tree.push_front(data);
|
||||
// ContextTree { tree: new_list }
|
||||
// }
|
||||
|
||||
// pub fn iter(&self) -> impl Iterator<Item = &Rc<Node<ContextElement<'r, 's>>>> {
|
||||
// self.tree.iter()
|
||||
// }
|
||||
|
||||
// #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
// pub fn check_exit_matcher(
|
||||
// &'r self,
|
||||
// i: 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.
|
||||
// let at_end_of_file = eof(i);
|
||||
// if at_end_of_file.is_ok() {
|
||||
// return at_end_of_file;
|
||||
// }
|
||||
|
||||
// let mut current_class_filter = ExitClass::Gamma;
|
||||
// for current_node in self.iter() {
|
||||
// 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(),
|
||||
// ))));
|
||||
// }
|
||||
|
||||
// /// Indicates if elements should consume the whitespace after them.
|
||||
// ///
|
||||
// /// Defaults to true.
|
||||
// pub fn should_consume_trailing_whitespace(&self) -> bool {
|
||||
// self._should_consume_trailing_whitespace().unwrap_or(true)
|
||||
// }
|
||||
|
||||
// fn _should_consume_trailing_whitespace(&self) -> Option<bool> {
|
||||
// for current_node in self.iter() {
|
||||
// let context_element = current_node.get_data();
|
||||
// match context_element {
|
||||
// ContextElement::ConsumeTrailingWhitespace(should) => {
|
||||
// return Some(*should);
|
||||
// }
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ContextElement<'r, 's> {
|
||||
/// Stores a parser that indicates that children should exit upon matching an exit matcher.
|
||||
ExitMatcherNode(ExitMatcherNode<'r>),
|
||||
|
||||
/// 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),
|
||||
|
||||
/// The contents of a radio target.
|
||||
///
|
||||
/// If any are found, this will force a 2nd parse through the
|
||||
/// 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>>>),
|
||||
}
|
||||
|
||||
pub struct ExitMatcherNode<'r> {
|
||||
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
|
||||
pub exit_matcher: &'r DynContextMatcher<'r>,
|
||||
pub class: ExitClass,
|
||||
}
|
||||
|
||||
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");
|
||||
formatter.field("class", &self.class.to_string());
|
||||
formatter.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalSettings<'s> {
|
||||
placeholder: Option<&'s str>,
|
||||
}
|
||||
|
||||
impl<'s> GlobalSettings<'s> {
|
||||
pub fn new() -> Self {
|
||||
GlobalSettings { placeholder: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Default for GlobalSettings<'s> {
|
||||
fn default() -> Self {
|
||||
GlobalSettings::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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 {
|
||||
Self {
|
||||
global_settings,
|
||||
tree,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn document_context(global_settings: &'s GlobalSettings<'s>) -> Self {
|
||||
Context::new(
|
||||
global_settings,
|
||||
List::new(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
exit_matcher: &document_end,
|
||||
class: ExitClass::Document,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 's> Default for Context<'r, 's> {
|
||||
fn default() -> Self {
|
||||
Context::new(
|
||||
GlobalSettings::default(),
|
||||
List::new(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
exit_matcher: &document_end,
|
||||
class: ExitClass::Document,
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn document_end<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
eof(input)
|
||||
}
|
||||
6
src/context/parser_with_context.rs
Normal file
6
src/context/parser_with_context.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
macro_rules! parser_with_context {
|
||||
($target:expr) => {
|
||||
move |context| move |i| $target(context, i)
|
||||
};
|
||||
}
|
||||
pub(crate) use parser_with_context;
|
||||
Reference in New Issue
Block a user