QoL changes
This commit is contained in:
parent
ce9effd465
commit
62e0aeecef
20 changed files with 181 additions and 103 deletions
|
@ -55,7 +55,7 @@ pub mod tests {
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
assert_eq!(validate_refname(&*doc, " abc ", true), Ok("abc"));
|
assert_eq!(validate_refname(&*doc, " abc ", true), Ok("abc"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -63,7 +63,9 @@ impl Variable for BaseVariable {
|
||||||
self.to_string(),
|
self.to_string(),
|
||||||
));
|
));
|
||||||
|
|
||||||
state.with_state(|new_state| new_state.parser.parse_into(new_state, source, document))
|
state.with_state(|new_state| {
|
||||||
|
let _ = new_state.parser.parse_into(new_state, source, document);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -680,7 +680,7 @@ fn fact(n: usize) -> usize
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
let borrow = doc.content().borrow();
|
let borrow = doc.content().borrow();
|
||||||
let found = borrow
|
let found = borrow
|
||||||
|
@ -726,7 +726,7 @@ fn fact(n: usize) -> usize
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
let borrow = doc.content().borrow();
|
let borrow = doc.content().borrow();
|
||||||
let found = borrow
|
let found = borrow
|
||||||
|
|
|
@ -123,7 +123,7 @@ COMMENT ::Test
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
|
|
@ -399,6 +399,8 @@ impl Rule for CustomStyleRule {
|
||||||
.custom_styles
|
.custom_styles
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(Rc::new(style));
|
.insert(Rc::new(style));
|
||||||
|
|
||||||
|
ctx.state.reset_match("Custom Style").unwrap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -454,6 +456,8 @@ impl Rule for CustomStyleRule {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.state.shared.custom_styles.borrow_mut().insert(Rc::new(style));
|
ctx.state.shared.custom_styles.borrow_mut().insert(Rc::new(style));
|
||||||
|
|
||||||
|
ctx.state.reset_match("Custom Style").unwrap();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -505,7 +509,7 @@ pre |styled| post °Hello°.
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
@ -549,7 +553,7 @@ pre [styled] post (Hello).
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
|
|
@ -161,7 +161,7 @@ impl RegexRule for ImportRule {
|
||||||
};
|
};
|
||||||
|
|
||||||
state.with_state(|new_state| {
|
state.with_state(|new_state| {
|
||||||
let import_doc = new_state.parser.parse(new_state, import, Some(document));
|
let (import_doc, _) = new_state.parser.parse(new_state, import, Some(document));
|
||||||
document.merge(import_doc.content(), import_doc.scope(), Some(&import_as));
|
document.merge(import_doc.content(), import_doc.scope(), Some(&import_as));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -895,7 +895,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Layout { token == LayoutToken::Begin, id == 0 };
|
Layout { token == LayoutToken::Begin, id == 0 };
|
||||||
|
@ -947,7 +947,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Layout { token == LayoutToken::Begin, id == 0 };
|
Layout { token == LayoutToken::Begin, id == 0 };
|
||||||
|
|
|
@ -284,7 +284,7 @@ Some [link](url).
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
@ -314,7 +314,7 @@ nml.link.push("**BOLD link**", "another url")
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
|
|
@ -463,7 +463,7 @@ mod tests {
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let state = ParserState::new(&parser, None);
|
let state = ParserState::new(&parser, None);
|
||||||
let doc = parser.parse(state, source, None);
|
let (doc, _) = parser.parse(state, source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
ListMarker { numbered == false, kind == MarkerKind::Open };
|
ListMarker { numbered == false, kind == MarkerKind::Open };
|
||||||
|
|
|
@ -548,7 +548,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
let borrow = doc.content().borrow();
|
let borrow = doc.content().borrow();
|
||||||
let group = borrow.first().as_ref().unwrap().as_container().unwrap();
|
let group = borrow.first().as_ref().unwrap().as_container().unwrap();
|
||||||
|
|
|
@ -165,7 +165,7 @@ Last paragraph
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
|
|
@ -283,7 +283,7 @@ Break{?[kind=block] Raw?}NewParagraph{?<b>?}
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph;
|
Paragraph;
|
||||||
|
@ -306,7 +306,7 @@ Break%<nml.raw.push("block", "Raw")>%NewParagraph%<nml.raw.push("inline", "<b>")
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph;
|
Paragraph;
|
||||||
|
|
|
@ -320,7 +320,7 @@ Evaluation: %<! make_ref("hello", "id")>%
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph;
|
Paragraph;
|
||||||
|
|
|
@ -438,7 +438,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Section { depth == 1, title == "1" };
|
Section { depth == 1, title == "1" };
|
||||||
|
@ -468,7 +468,7 @@ nml.section.push("6", 6, "", "refname")
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Section { depth == 1, title == "1" };
|
Section { depth == 1, title == "1" };
|
||||||
|
@ -495,18 +495,16 @@ nml.section.push("6", 6, "", "refname")
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let state = ParserState::new(&parser, None);
|
let state = ParserState::new(&parser, None);
|
||||||
let _ = parser.parse(state, source, None);
|
let (_, state) = parser.parse(state, source, None);
|
||||||
|
|
||||||
// TODO2
|
|
||||||
/*
|
|
||||||
let style = state.shared
|
let style = state.shared
|
||||||
.styles
|
.styles
|
||||||
.current_style(section_style::STYLE_KEY)
|
.borrow()
|
||||||
|
.current(section_style::STYLE_KEY)
|
||||||
.downcast_rc::<SectionStyle>()
|
.downcast_rc::<SectionStyle>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(style.link_pos, SectionLinkPos::None);
|
assert_eq!(style.link_pos, SectionLinkPos::None);
|
||||||
assert_eq!(style.link, ["a".to_string(), "b".to_string(), "c".to_string()]);
|
assert_eq!(style.link, ["a".to_string(), "b".to_string(), "c".to_string()]);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,7 @@ __`UNDERLINE+EM`__
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
|
|
@ -450,7 +450,7 @@ $[kind=block,env=another] e^{i\pi}=-1$
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) };
|
Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) };
|
||||||
|
@ -472,7 +472,7 @@ $[env=another] e^{i\pi}=-1$
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(ParserState::new(&parser, None), source, None);
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
validate_document!(doc.content().borrow(), 0,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
|
|
|
@ -56,7 +56,7 @@ fn parse(
|
||||||
|
|
||||||
// Parse
|
// Parse
|
||||||
let source = SourceFile::new(input.to_string(), None).unwrap();
|
let source = SourceFile::new(input.to_string(), None).unwrap();
|
||||||
let doc = parser.parse(ParserState::new(parser, None), Rc::new(source), None);
|
let (doc, _) = parser.parse(ParserState::new(parser, None), Rc::new(source), None);
|
||||||
|
|
||||||
if debug_opts.contains(&"ast".to_string()) {
|
if debug_opts.contains(&"ast".to_string()) {
|
||||||
println!("-- BEGIN AST DEBUGGING --");
|
println!("-- BEGIN AST DEBUGGING --");
|
||||||
|
|
|
@ -50,12 +50,12 @@ impl Parser for LangParser {
|
||||||
|
|
||||||
fn has_error(&self) -> bool { *self.err_flag.borrow() }
|
fn has_error(&self) -> bool { *self.err_flag.borrow() }
|
||||||
|
|
||||||
fn parse<'a>(
|
fn parse<'p, 'a, 'doc>(
|
||||||
&self,
|
&'p self,
|
||||||
state: ParserState,
|
state: ParserState<'p, 'a>,
|
||||||
source: Rc<dyn Source>,
|
source: Rc<dyn Source>,
|
||||||
parent: Option<&'a dyn Document<'a>>,
|
parent: Option<&'doc dyn Document<'doc>>,
|
||||||
) -> Box<dyn Document<'a> + 'a> {
|
) -> (Box<dyn Document<'doc> + 'doc>, ParserState<'p, 'a>) {
|
||||||
let doc = LangDocument::new(source.clone(), parent);
|
let doc = LangDocument::new(source.clone(), parent);
|
||||||
|
|
||||||
let content = source.content();
|
let content = source.content();
|
||||||
|
@ -105,7 +105,6 @@ impl Parser for LangParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule States
|
// Rule States
|
||||||
|
|
||||||
self.handle_reports(state.shared.rule_state.borrow_mut().on_scope_end(
|
self.handle_reports(state.shared.rule_state.borrow_mut().on_scope_end(
|
||||||
&state,
|
&state,
|
||||||
&doc,
|
&doc,
|
||||||
|
@ -120,15 +119,15 @@ impl Parser for LangParser {
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Box::new(doc);
|
return (Box::new(doc), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_into<'a>(
|
fn parse_into<'p, 'a, 'doc>(
|
||||||
&self,
|
&'p self,
|
||||||
state: ParserState,
|
state: ParserState<'p, 'a>,
|
||||||
source: Rc<dyn Source>,
|
source: Rc<dyn Source>,
|
||||||
document: &'a dyn Document<'a>,
|
document: &'doc dyn Document<'doc>,
|
||||||
) {
|
) -> ParserState<'p, 'a> {
|
||||||
let content = source.content();
|
let content = source.content();
|
||||||
let mut cursor = Cursor::new(0usize, source.clone());
|
let mut cursor = Cursor::new(0usize, source.clone());
|
||||||
|
|
||||||
|
@ -164,6 +163,7 @@ impl Parser for LangParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
// State
|
// State
|
||||||
//self.handle_reports(source.clone(),
|
//self.handle_reports(source.clone(),
|
||||||
// self.state_mut().on_scope_end(&self, &document, super::state::Scope::DOCUMENT));
|
// self.state_mut().on_scope_end(&self, &document, super::state::Scope::DOCUMENT));
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
use ariadne::Label;
|
||||||
|
use ariadne::Report;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use ariadne::Label;
|
|
||||||
use ariadne::Report;
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::customstyle::CustomStyleHolder;
|
use super::customstyle::CustomStyleHolder;
|
||||||
|
@ -55,7 +55,7 @@ impl ReportColors {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state that is shared with the state's children
|
/// The state that is shared with the state's childre
|
||||||
pub struct SharedState {
|
pub struct SharedState {
|
||||||
pub rule_state: RefCell<RuleStateHolder>,
|
pub rule_state: RefCell<RuleStateHolder>,
|
||||||
|
|
||||||
|
@ -84,9 +84,11 @@ impl SharedState {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register default kernel
|
// Register default kernel
|
||||||
s.kernels.borrow_mut()
|
s.kernels
|
||||||
|
.borrow_mut()
|
||||||
.insert("main".to_string(), Kernel::new(parser));
|
.insert("main".to_string(), Kernel::new(parser));
|
||||||
|
|
||||||
|
// Default styles & layouts
|
||||||
parser.rules().iter().for_each(|rule| {
|
parser.rules().iter().for_each(|rule| {
|
||||||
rule.register_styles(&mut *s.styles.borrow_mut());
|
rule.register_styles(&mut *s.styles.borrow_mut());
|
||||||
rule.register_layouts(&mut *s.layouts.borrow_mut());
|
rule.register_layouts(&mut *s.layouts.borrow_mut());
|
||||||
|
@ -118,9 +120,9 @@ pub struct ParserState<'a, 'b> {
|
||||||
impl<'a, 'b> ParserState<'a, 'b> {
|
impl<'a, 'b> ParserState<'a, 'b> {
|
||||||
/// Constructs a new state for a given parser with an optional parent
|
/// Constructs a new state for a given parser with an optional parent
|
||||||
///
|
///
|
||||||
/// Parent should be None when parsing a brand new document.
|
/// Parent should be None when parsing a brand new document. If you have to
|
||||||
/// If you have to set the parent to Some(..) (e.g for imports or sub-document),
|
/// set the parent to Some(..) (e.g for imports or sub-document), be sure
|
||||||
/// be sure to use the [`ParserState::with_state`] method instead, this create a
|
/// to use the [`ParserState::with_state`] method instead, this create a
|
||||||
/// RAII lived state for use within bounded lifetime.
|
/// RAII lived state for use within bounded lifetime.
|
||||||
pub fn new(parser: &'a dyn Parser, parent: Option<&'a ParserState<'a, 'b>>) -> Self {
|
pub fn new(parser: &'a dyn Parser, parent: Option<&'a ParserState<'a, 'b>>) -> Self {
|
||||||
let matches = parser.rules().iter().map(|_| (0, None)).collect::<Vec<_>>();
|
let matches = parser.rules().iter().map(|_| (0, None)).collect::<Vec<_>>();
|
||||||
|
@ -153,24 +155,37 @@ impl<'a, 'b> ParserState<'a, 'b> {
|
||||||
/// Updates matches from a given start position e.g [`Cursor`]
|
/// Updates matches from a given start position e.g [`Cursor`]
|
||||||
///
|
///
|
||||||
/// # Return
|
/// # Return
|
||||||
|
///
|
||||||
/// 1. The cursor position after updating the matches
|
/// 1. The cursor position after updating the matches
|
||||||
/// 2. (Optional) The winning match with it's match data
|
/// 2. (Optional) The winning match with it's match data
|
||||||
|
/// If the winning match is None, it means that the document has no more
|
||||||
|
/// rule to match. I.e The rest of the content should be added as a
|
||||||
|
/// [`Text`] element.
|
||||||
|
/// The match data should be passed to the [`Rule::on_match`] method.
|
||||||
///
|
///
|
||||||
/// If the winning match is None, it means that the document has no more rule to match
|
/// # Strategy
|
||||||
/// I.E The rest of the content should be added as a [`Text`] element.
|
///
|
||||||
/// The match data should be passed to the [`Rule::on_match`] method
|
/// This function call [`Rule::next_match`] on the rules defined for the
|
||||||
pub fn update_matches(
|
/// parser. It then takes the rule that has the closest `next_match` and
|
||||||
&self,
|
/// returns it. If next_match starts on an escaped character i.e `\\`,
|
||||||
cursor: &Cursor,
|
/// then it starts over to find another match for that rule.
|
||||||
) -> (Cursor, Option<(usize, Box<dyn Any>)>) {
|
/// In case multiple rules have the same `next_match`, the rules that are
|
||||||
|
/// defined first in the parser are prioritized. See [Parser::add_rule] for
|
||||||
|
/// information on how to prioritize rules.
|
||||||
|
///
|
||||||
|
/// Notes that the result of every call to [`Rule::next_match`] gets stored
|
||||||
|
/// in a table: [`ParserState::matches`]. Until the cursor steps over a
|
||||||
|
/// position in the table, `next_match` won't be called.
|
||||||
|
pub fn update_matches(&self, cursor: &Cursor) -> (Cursor, Option<(usize, Box<dyn Any>)>) {
|
||||||
let mut matches_borrow = self.matches.borrow_mut();
|
let mut matches_borrow = self.matches.borrow_mut();
|
||||||
|
|
||||||
self.parser.rules()
|
self.parser
|
||||||
|
.rules()
|
||||||
.iter()
|
.iter()
|
||||||
.zip(matches_borrow.iter_mut())
|
.zip(matches_borrow.iter_mut())
|
||||||
.for_each(|(rule, (matched_at, match_data))| {
|
.for_each(|(rule, (matched_at, match_data))| {
|
||||||
// Don't upate if not stepped over yet
|
// Don't upate if not stepped over yet
|
||||||
if *matched_at > cursor.pos && rule.downcast_ref::<CustomStyleRule>().is_none() {
|
if *matched_at > cursor.pos {
|
||||||
// TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule
|
// TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -215,15 +230,18 @@ impl<'a, 'b> ParserState<'a, 'b> {
|
||||||
.map(|(winner, (pos, _))| (winner, *pos))
|
.map(|(winner, (pos, _))| (winner, *pos))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if next_pos == usize::MAX // No rule has matched
|
if next_pos == usize::MAX
|
||||||
|
// No rule has matched
|
||||||
{
|
{
|
||||||
let content = cursor.source.content();
|
let content = cursor.source.content();
|
||||||
// No winners, i.e no matches left
|
// No winners, i.e no matches left
|
||||||
return (cursor.at(content.len()), None);
|
return (cursor.at(content.len()), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (cursor.at(next_pos),
|
return (
|
||||||
Some((winner, matches_borrow[winner].1.take().unwrap())))
|
cursor.at(next_pos),
|
||||||
|
Some((winner, matches_borrow[winner].1.take().unwrap())),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an [`Element`] to the [`Document`]
|
/// Add an [`Element`] to the [`Document`]
|
||||||
|
@ -244,15 +262,53 @@ impl<'a, 'b> ParserState<'a, 'b> {
|
||||||
} else {
|
} else {
|
||||||
// Process paragraph events
|
// Process paragraph events
|
||||||
if doc.last_element::<Paragraph>().is_some_and(|_| true) {
|
if doc.last_element::<Paragraph>().is_some_and(|_| true) {
|
||||||
self.parser.handle_reports(
|
self.parser
|
||||||
self.shared.rule_state.borrow_mut()
|
.handle_reports(self.shared.rule_state.borrow_mut().on_scope_end(
|
||||||
.on_scope_end(&self, doc, super::state::Scope::PARAGRAPH),
|
&self,
|
||||||
);
|
doc,
|
||||||
|
super::state::Scope::PARAGRAPH,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.push(elem);
|
doc.push(elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resets the position and the match_data for a given rule. This is used
|
||||||
|
/// in order to have 'dynamic' rules that may not match at first, but their
|
||||||
|
/// matching rule is modified through the parsing process.
|
||||||
|
///
|
||||||
|
/// This function also recursively calls itself on it's `parent`, in order
|
||||||
|
/// to fully reset the match.
|
||||||
|
///
|
||||||
|
/// See [`CustomStyleRule`] for an example of how this is used.
|
||||||
|
///
|
||||||
|
/// # Error
|
||||||
|
///
|
||||||
|
/// Returns an error if `rule_name` was not found in the parser's ruleset.
|
||||||
|
pub fn reset_match(&self, rule_name: &str) -> Result<(), String>
|
||||||
|
{
|
||||||
|
if self.parser.rules().iter()
|
||||||
|
.zip(self.matches.borrow_mut().iter_mut())
|
||||||
|
.try_for_each(|(rule, (match_pos, match_data))| {
|
||||||
|
if rule.name() != rule_name { return Ok(()) }
|
||||||
|
|
||||||
|
*match_pos = 0;
|
||||||
|
match_data.take();
|
||||||
|
Err(())
|
||||||
|
}).is_ok()
|
||||||
|
{
|
||||||
|
return Err(format!("Could not find rule: {rule_name}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resurcively reset
|
||||||
|
if let Some(parent) = self.parent
|
||||||
|
{
|
||||||
|
return parent.reset_match(rule_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Parser {
|
pub trait Parser {
|
||||||
|
@ -273,32 +329,47 @@ pub trait Parser {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This method will not fail because we try to optimistically recover from parsing errors.
|
/// This method will not fail because we try to optimistically recover from
|
||||||
/// However the resulting document should not get compiled if an error has happened
|
/// parsing errors. However the resulting document should not get compiled
|
||||||
/// see [`Parser::has_error()`] for reference
|
/// if an error has happenedn, see [`Parser::has_error()`] for reference
|
||||||
fn parse<'a>(
|
///
|
||||||
&self,
|
/// # Returns
|
||||||
state: ParserState,
|
///
|
||||||
|
/// This method returns the resulting [`Document`] after psrsing `source`,
|
||||||
|
/// note that the [`ParserState`] is only meant to perform testing and not
|
||||||
|
/// meant to be reused.
|
||||||
|
fn parse<'p, 'a, 'doc>(
|
||||||
|
&'p self,
|
||||||
|
state: ParserState<'p, 'a>,
|
||||||
source: Rc<dyn Source>,
|
source: Rc<dyn Source>,
|
||||||
parent: Option<&'a dyn Document<'a>>,
|
parent: Option<&'doc dyn Document<'doc>>,
|
||||||
) -> Box<dyn Document<'a> + 'a>;
|
) -> (Box<dyn Document<'doc> + 'doc>, ParserState<'p, 'a>);
|
||||||
|
|
||||||
/// Parse [`Source`] into an already existing [`Document`]
|
/// Parse [`Source`] into an already existing [`Document`]
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// This method will not fail because we try to optimistically recover from parsing errors.
|
/// This method will not fail because we try to optimistically recover from
|
||||||
/// However the resulting document should not get compiled if an error has happened
|
/// parsing errors. However the resulting document should not get compiled
|
||||||
/// see [`Parser::has_error()`] for reference
|
/// if an error has happened see [`Parser::has_error()`] for reference
|
||||||
fn parse_into<'a>(&self,
|
///
|
||||||
state: ParserState,
|
/// # Returns
|
||||||
source: Rc<dyn Source>, document: &'a dyn Document<'a>);
|
///
|
||||||
|
/// The returned [`ParserState`] is not meant to be reused, it's meant for
|
||||||
|
/// testing.
|
||||||
|
fn parse_into<'p, 'a, 'doc>(
|
||||||
|
&'p self,
|
||||||
|
state: ParserState<'p, 'a>,
|
||||||
|
source: Rc<dyn Source>,
|
||||||
|
document: &'doc dyn Document<'doc>,
|
||||||
|
) -> ParserState<'p, 'a>;
|
||||||
|
|
||||||
fn add_rule(
|
/// Adds a rule to the parser.
|
||||||
&mut self,
|
///
|
||||||
rule: Box<dyn Rule>,
|
/// # Warning
|
||||||
after: Option<&'static str>,
|
///
|
||||||
) -> Result<(), String> {
|
/// This method must not be called if a [`ParserState`] for this parser exists.
|
||||||
|
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> {
|
||||||
if let Some(_) = self
|
if let Some(_) = self
|
||||||
.rules()
|
.rules()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -312,12 +383,12 @@ pub trait Parser {
|
||||||
|
|
||||||
// Try to insert after
|
// Try to insert after
|
||||||
if let Some(after) = after {
|
if let Some(after) = after {
|
||||||
let index =
|
let index = self
|
||||||
self.rules()
|
.rules()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, rule)| rule.name() == after)
|
.find(|(_, rule)| rule.name() == after)
|
||||||
.map(|(idx, _)| idx);
|
.map(|(idx, _)| idx);
|
||||||
|
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
self.rules_mut().insert(index, rule);
|
self.rules_mut().insert(index, rule);
|
||||||
|
@ -331,10 +402,9 @@ pub trait Parser {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_reports(
|
/// Handles the reports produced by parsing. The default is to output them
|
||||||
&self,
|
/// to stderr, but you are free to modify it.
|
||||||
reports: Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>,
|
fn handle_reports(&self, reports: Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||||
) {
|
|
||||||
for mut report in reports {
|
for mut report in reports {
|
||||||
let mut sources: HashSet<Rc<dyn Source>> = HashSet::new();
|
let mut sources: HashSet<Rc<dyn Source>> = HashSet::new();
|
||||||
fn recurse_source(sources: &mut HashSet<Rc<dyn Source>>, source: Rc<dyn Source>) {
|
fn recurse_source(sources: &mut HashSet<Rc<dyn Source>>, source: Rc<dyn Source>) {
|
||||||
|
@ -364,8 +434,8 @@ pub trait Parser {
|
||||||
if let Some(_s) = source.downcast_ref::<SourceFile>() {
|
if let Some(_s) = source.downcast_ref::<SourceFile>() {
|
||||||
report.labels.push(
|
report.labels.push(
|
||||||
Label::new((location.source(), location.start() + 1..location.end()))
|
Label::new((location.source(), location.start() + 1..location.end()))
|
||||||
.with_message("In file included from here")
|
.with_message("In file included from here")
|
||||||
.with_order(-1),
|
.with_order(-1),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -373,12 +443,12 @@ pub trait Parser {
|
||||||
let start = location.start()
|
let start = location.start()
|
||||||
+ (location.source().content().as_bytes()[location.start()]
|
+ (location.source().content().as_bytes()[location.start()]
|
||||||
== '\n' as u8)
|
== '\n' as u8)
|
||||||
.then_some(1)
|
.then_some(1)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
report.labels.push(
|
report.labels.push(
|
||||||
Label::new((location.source(), start..location.end()))
|
Label::new((location.source(), start..location.end()))
|
||||||
.with_message("In evaluation of")
|
.with_message("In evaluation of")
|
||||||
.with_order(-1),
|
.with_order(-1),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,7 @@ pub fn parse_paragraph<'a>(
|
||||||
new_state
|
new_state
|
||||||
.parser
|
.parser
|
||||||
.parse(new_state, source.clone(), Some(document))
|
.parse(new_state, source.clone(), Some(document))
|
||||||
|
.0
|
||||||
});
|
});
|
||||||
if parsed.content().borrow().len() > 1 {
|
if parsed.content().borrow().len() > 1 {
|
||||||
return Err("Parsed document contains more than a single paragraph");
|
return Err("Parsed document contains more than a single paragraph");
|
||||||
|
@ -422,20 +423,23 @@ mod tests {
|
||||||
(&doc as &dyn Document)
|
(&doc as &dyn Document)
|
||||||
.last_element_mut::<Paragraph>()
|
.last_element_mut::<Paragraph>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push(Box::new(Comment::new(tok.clone(), "COMMENT".to_string())));
|
.push(Box::new(Comment::new(tok.clone(), "COMMENT".to_string())))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(process_text(&doc, "\na"), "a");
|
assert_eq!(process_text(&doc, "\na"), "a");
|
||||||
|
|
||||||
// A space is appended as previous element is inline
|
// A space is appended as previous element is inline
|
||||||
(&doc as &dyn Document)
|
(&doc as &dyn Document)
|
||||||
.last_element_mut::<Paragraph>()
|
.last_element_mut::<Paragraph>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push(Box::new(Text::new(tok.clone(), "TEXT".to_string())));
|
.push(Box::new(Text::new(tok.clone(), "TEXT".to_string())))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(process_text(&doc, "\na"), " a");
|
assert_eq!(process_text(&doc, "\na"), " a");
|
||||||
|
|
||||||
(&doc as &dyn Document)
|
(&doc as &dyn Document)
|
||||||
.last_element_mut::<Paragraph>()
|
.last_element_mut::<Paragraph>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push(Box::new(Style::new(tok.clone(), 0, false)));
|
.push(Box::new(Style::new(tok.clone(), 0, false)))
|
||||||
|
.unwrap();
|
||||||
assert_eq!(process_text(&doc, "\na"), " a");
|
assert_eq!(process_text(&doc, "\na"), " a");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue