From 4784921bb880e2724a0f2a2e39b5aed66787e841 Mon Sep 17 00:00:00 2001 From: ef3d0c3e Date: Thu, 8 Aug 2024 14:12:16 +0200 Subject: [PATCH] Automatic rules registration --- src/elements/code.rs | 2 + src/elements/comment.rs | 3 ++ src/elements/customstyle.rs | 6 +++ src/elements/elemstyle.rs | 2 + src/elements/graphviz.rs | 2 + src/elements/import.rs | 2 + src/elements/layout.rs | 2 + src/elements/link.rs | 2 + src/elements/list.rs | 2 + src/elements/media.rs | 2 + src/elements/mod.rs | 1 - src/elements/paragraph.rs | 4 +- src/elements/raw.rs | 2 + src/elements/reference.rs | 2 + src/elements/registrar.rs | 46 ----------------- src/elements/script.rs | 2 + src/elements/section.rs | 2 + src/elements/style.rs | 2 + src/elements/tex.rs | 2 + src/elements/text.rs | 11 ++++- src/elements/variable.rs | 4 ++ src/parser/langparser.rs | 7 +-- src/parser/parser.rs | 22 +-------- src/parser/rule.rs | 99 ++++++++++++++++++++++++++++++++++++- 24 files changed, 157 insertions(+), 74 deletions(-) delete mode 100644 src/elements/registrar.rs diff --git a/src/elements/code.rs b/src/elements/code.rs index 05921c6..f7853fd 100644 --- a/src/elements/code.rs +++ b/src/elements/code.rs @@ -296,6 +296,7 @@ impl Element for Code { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct CodeRule { re: [Regex; 2], properties: PropertyParser, @@ -330,6 +331,7 @@ impl CodeRule { impl RegexRule for CodeRule { fn name(&self) -> &'static str { "Code" } + fn previous(&self) -> Option<&'static str> { Some("List") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/comment.rs b/src/elements/comment.rs index 982f7d4..a5df792 100644 --- a/src/elements/comment.rs +++ b/src/elements/comment.rs @@ -38,6 +38,7 @@ impl Element for Comment { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct CommentRule { re: [Regex; 1], } @@ -53,6 +54,8 @@ impl CommentRule { impl RegexRule for CommentRule { fn name(&self) -> &'static str { "Comment" } + fn previous(&self) -> Option<&'static str> { None } + fn regexes(&self) -> &[Regex] { &self.re } fn on_regex_match<'a>( diff --git a/src/elements/customstyle.rs b/src/elements/customstyle.rs index e06f596..06c94a9 100644 --- a/src/elements/customstyle.rs +++ b/src/elements/customstyle.rs @@ -178,10 +178,16 @@ impl RuleState for CustomStyleState { static STATE_NAME: &'static str = "elements.custom_style"; +#[auto_registry::auto_registry(registry = "rules")] pub struct CustomStyleRule; +impl CustomStyleRule { + pub fn new() -> Self { Self{} } +} + impl Rule for CustomStyleRule { fn name(&self) -> &'static str { "Custom Style" } + fn previous(&self) -> Option<&'static str> { Some("Style") } fn next_match(&self, state: &ParserState, cursor: &Cursor) -> Option<(usize, Box)> { let content = cursor.source.content(); diff --git a/src/elements/elemstyle.rs b/src/elements/elemstyle.rs index 51a8dcd..2cfab00 100644 --- a/src/elements/elemstyle.rs +++ b/src/elements/elemstyle.rs @@ -21,6 +21,7 @@ use crate::parser::rule::Rule; use crate::parser::source::Cursor; use crate::parser::source::Source; +#[auto_registry::auto_registry(registry = "rules")] pub struct ElemStyleRule { start_re: Regex, } @@ -58,6 +59,7 @@ impl ElemStyleRule { impl Rule for ElemStyleRule { fn name(&self) -> &'static str { "Element Style" } + fn previous(&self) -> Option<&'static str> { Some("Script") } fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box)> { self.start_re diff --git a/src/elements/graphviz.rs b/src/elements/graphviz.rs index c0ae45d..378e33e 100644 --- a/src/elements/graphviz.rs +++ b/src/elements/graphviz.rs @@ -146,6 +146,7 @@ impl Element for Graphviz { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct GraphRule { re: [Regex; 1], properties: PropertyParser, @@ -178,6 +179,7 @@ impl GraphRule { impl RegexRule for GraphRule { fn name(&self) -> &'static str { "Graph" } + fn previous(&self) -> Option<&'static str> { Some("Tex") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/import.rs b/src/elements/import.rs index dc7c682..57386c7 100644 --- a/src/elements/import.rs +++ b/src/elements/import.rs @@ -17,6 +17,7 @@ use std::rc::Rc; use super::paragraph::Paragraph; +#[auto_registry::auto_registry(registry = "rules")] pub struct ImportRule { re: [Regex; 1], } @@ -40,6 +41,7 @@ impl ImportRule { impl RegexRule for ImportRule { fn name(&self) -> &'static str { "Import" } + fn previous(&self) -> Option<&'static str> { Some("Paragraph") } fn regexes(&self) -> &[Regex] { &self.re } diff --git a/src/elements/layout.rs b/src/elements/layout.rs index 05262e2..3128634 100644 --- a/src/elements/layout.rs +++ b/src/elements/layout.rs @@ -284,6 +284,7 @@ impl RuleState for LayoutState { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct LayoutRule { re: [Regex; 3], } @@ -378,6 +379,7 @@ static STATE_NAME: &'static str = "elements.layout"; impl RegexRule for LayoutRule { fn name(&self) -> &'static str { "Layout" } + fn previous(&self) -> Option<&'static str> { Some("Media") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/link.rs b/src/elements/link.rs index cced54c..ab388d1 100644 --- a/src/elements/link.rs +++ b/src/elements/link.rs @@ -71,6 +71,7 @@ impl ContainerElement for Link { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct LinkRule { re: [Regex; 1], } @@ -85,6 +86,7 @@ impl LinkRule { impl RegexRule for LinkRule { fn name(&self) -> &'static str { "Link" } + fn previous(&self) -> Option<&'static str> { Some("Link") } fn regexes(&self) -> &[Regex] { &self.re } diff --git a/src/elements/list.rs b/src/elements/list.rs index f896952..ad9058b 100644 --- a/src/elements/list.rs +++ b/src/elements/list.rs @@ -106,6 +106,7 @@ impl ContainerElement for ListEntry { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct ListRule { start_re: Regex, continue_re: Regex, @@ -249,6 +250,7 @@ impl ListRule { impl Rule for ListRule { fn name(&self) -> &'static str { "List" } + fn previous(&self) -> Option<&'static str> { Some("Raw") } fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box)> { self.start_re diff --git a/src/elements/media.rs b/src/elements/media.rs index a59a6ce..d4d49ef 100644 --- a/src/elements/media.rs +++ b/src/elements/media.rs @@ -221,6 +221,7 @@ impl ReferenceableElement for Medium { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct MediaRule { re: [Regex; 1], properties: PropertyParser, @@ -325,6 +326,7 @@ impl MediaRule { impl RegexRule for MediaRule { fn name(&self) -> &'static str { "Media" } + fn previous(&self) -> Option<&'static str> { Some("Graph") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/mod.rs b/src/elements/mod.rs index abfd1a5..ece4e6c 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -9,7 +9,6 @@ pub mod media; pub mod paragraph; pub mod raw; pub mod reference; -pub mod registrar; pub mod script; pub mod section; pub mod style; diff --git a/src/elements/paragraph.rs b/src/elements/paragraph.rs index 24b401c..a631390 100644 --- a/src/elements/paragraph.rs +++ b/src/elements/paragraph.rs @@ -91,6 +91,7 @@ impl ContainerElement for Paragraph { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct ParagraphRule { re: Regex, } @@ -104,7 +105,8 @@ impl ParagraphRule { } impl Rule for ParagraphRule { - fn name(&self) -> &'static str { "Paragraphing" } + fn name(&self) -> &'static str { "Paragraph" } + fn previous(&self) -> Option<&'static str> { Some("Comment") } fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box)> { self.re diff --git a/src/elements/raw.rs b/src/elements/raw.rs index ba939de..bbb34d1 100644 --- a/src/elements/raw.rs +++ b/src/elements/raw.rs @@ -44,6 +44,7 @@ impl Element for Raw { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct RawRule { re: [Regex; 1], properties: PropertyParser, @@ -72,6 +73,7 @@ impl RawRule { impl RegexRule for RawRule { fn name(&self) -> &'static str { "Raw" } + fn previous(&self) -> Option<&'static str> { Some("Variable Substitution") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/reference.rs b/src/elements/reference.rs index cd09186..026ddee 100644 --- a/src/elements/reference.rs +++ b/src/elements/reference.rs @@ -62,6 +62,7 @@ impl Element for Reference { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct ReferenceRule { re: [Regex; 1], properties: PropertyParser, @@ -127,6 +128,7 @@ impl ReferenceRule { impl RegexRule for ReferenceRule { fn name(&self) -> &'static str { "Reference" } + fn previous(&self) -> Option<&'static str> { Some("Text") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/registrar.rs b/src/elements/registrar.rs deleted file mode 100644 index 01a4251..0000000 --- a/src/elements/registrar.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::parser::parser::Parser; - -use super::code::CodeRule; -use super::comment::CommentRule; -use super::elemstyle::ElemStyleRule; -use super::graphviz::GraphRule; -use super::import::ImportRule; -use super::layout::LayoutRule; -use super::link::LinkRule; -use super::list::ListRule; -use super::media::MediaRule; -use super::paragraph::ParagraphRule; -use super::raw::RawRule; -use super::script::ScriptRule; -use super::section::SectionRule; -use super::style::StyleRule; -use super::customstyle::CustomStyleRule; -use super::tex::TexRule; -use super::text::TextRule; -use super::variable::VariableRule; -use super::variable::VariableSubstitutionRule; -use super::reference::ReferenceRule; - -pub fn register(parser: &mut P) { - parser.add_rule(Box::new(CommentRule::new()), None).unwrap(); - parser.add_rule(Box::new(ParagraphRule::new()), None).unwrap(); - parser.add_rule(Box::new(ImportRule::new()), None).unwrap(); - parser.add_rule(Box::new(ScriptRule::new()), None).unwrap(); - parser.add_rule(Box::new(ElemStyleRule::new()), None).unwrap(); - parser.add_rule(Box::new(VariableRule::new()), None).unwrap(); - parser.add_rule(Box::new(VariableSubstitutionRule::new()), None).unwrap(); - parser.add_rule(Box::new(RawRule::new()), None).unwrap(); - parser.add_rule(Box::new(ListRule::new()), None).unwrap(); - parser.add_rule(Box::new(CodeRule::new()), None).unwrap(); - parser.add_rule(Box::new(TexRule::new()), None).unwrap(); - parser.add_rule(Box::new(GraphRule::new()), None).unwrap(); - parser.add_rule(Box::new(MediaRule::new()), None).unwrap(); - parser.add_rule(Box::new(LayoutRule::new()), None).unwrap(); - - parser.add_rule(Box::new(StyleRule::new()), None).unwrap(); - parser.add_rule(Box::new(CustomStyleRule{}), None).unwrap(); - parser.add_rule(Box::new(SectionRule::new()), None).unwrap(); - parser.add_rule(Box::new(LinkRule::new()), None).unwrap(); - parser.add_rule(Box::new(TextRule::default()), None).unwrap(); - parser.add_rule(Box::new(ReferenceRule::new()), None).unwrap(); -} diff --git a/src/elements/script.rs b/src/elements/script.rs index 374ea1e..11d3ea2 100644 --- a/src/elements/script.rs +++ b/src/elements/script.rs @@ -20,6 +20,7 @@ use std::rc::Rc; use super::text::Text; +#[auto_registry::auto_registry(registry = "rules")] pub struct ScriptRule { re: [Regex; 2], eval_kinds: [(&'static str, &'static str); 3], @@ -77,6 +78,7 @@ impl ScriptRule { impl RegexRule for ScriptRule { fn name(&self) -> &'static str { "Script" } + fn previous(&self) -> Option<&'static str> { Some("Import") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/section.rs b/src/elements/section.rs index 52ff5cc..3711b73 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -135,6 +135,7 @@ impl ReferenceableElement for Section { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct SectionRule { re: [Regex; 1], } @@ -155,6 +156,7 @@ pub mod section_kind { impl RegexRule for SectionRule { fn name(&self) -> &'static str { "Section" } + fn previous(&self) -> Option<&'static str> { Some("Custom Style") } fn regexes(&self) -> &[Regex] { &self.re } diff --git a/src/elements/style.rs b/src/elements/style.rs index cd65631..bd5fb04 100644 --- a/src/elements/style.rs +++ b/src/elements/style.rs @@ -132,6 +132,7 @@ impl RuleState for StyleState { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct StyleRule { re: [Regex; 4], } @@ -157,6 +158,7 @@ static STATE_NAME: &'static str = "elements.style"; impl RegexRule for StyleRule { fn name(&self) -> &'static str { "Style" } + fn previous(&self) -> Option<&'static str> { Some("Layout") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/tex.rs b/src/elements/tex.rs index b6b7fb8..e6ba28a 100644 --- a/src/elements/tex.rs +++ b/src/elements/tex.rs @@ -219,6 +219,7 @@ impl Element for Tex { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct TexRule { re: [Regex; 2], properties: PropertyParser, @@ -296,6 +297,7 @@ impl TexRule { impl RegexRule for TexRule { fn name(&self) -> &'static str { "Tex" } + fn previous(&self) -> Option<&'static str> { Some("Code") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/elements/text.rs b/src/elements/text.rs index 99f32cd..58e2d38 100644 --- a/src/elements/text.rs +++ b/src/elements/text.rs @@ -42,13 +42,20 @@ impl Element for Text { } } -#[derive(Default)] +#[auto_registry::auto_registry(registry = "rules")] pub struct TextRule; +impl TextRule { + pub fn new() -> Self { Self {} } +} + impl Rule for TextRule { fn name(&self) -> &'static str { "Text" } + fn previous(&self) -> Option<&'static str> { Some("Link") } - fn next_match(&self, _state: &ParserState, _cursor: &Cursor) -> Option<(usize, Box)> { None } + fn next_match(&self, _state: &ParserState, _cursor: &Cursor) -> Option<(usize, Box)> { + None + } fn on_match( &self, diff --git a/src/elements/variable.rs b/src/elements/variable.rs index 5768eae..a62ae5b 100644 --- a/src/elements/variable.rs +++ b/src/elements/variable.rs @@ -37,6 +37,7 @@ impl FromStr for VariableKind { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct VariableRule { re: [Regex; 1], kinds: Vec<(String, String)>, @@ -117,6 +118,7 @@ impl VariableRule { impl RegexRule for VariableRule { fn name(&self) -> &'static str { "Variable" } + fn previous(&self) -> Option<&'static str> { Some("Element Style") } fn regexes(&self) -> &[Regex] { &self.re } @@ -295,6 +297,7 @@ impl RegexRule for VariableRule { } } +#[auto_registry::auto_registry(registry = "rules")] pub struct VariableSubstitutionRule { re: [Regex; 1], } @@ -309,6 +312,7 @@ impl VariableSubstitutionRule { impl RegexRule for VariableSubstitutionRule { fn name(&self) -> &'static str { "Variable Substitution" } + fn previous(&self) -> Option<&'static str> { Some("Variable") } fn regexes(&self) -> &[regex::Regex] { &self.re } diff --git a/src/parser/langparser.rs b/src/parser/langparser.rs index 3ba7e21..851f87e 100644 --- a/src/parser/langparser.rs +++ b/src/parser/langparser.rs @@ -4,7 +4,6 @@ use std::rc::Rc; use crate::document::document::Document; use crate::document::element::DocumentEnd; use crate::document::langdocument::LangDocument; -use crate::elements::registrar::register; use crate::elements::text::Text; use super::parser::Parser; @@ -35,8 +34,10 @@ impl LangParser { }; // Register rules - // TODO: use https://docs.rs/inventory/latest/inventory/ - register(&mut s); + for rule in super::rule::get_rule_registry() + { + s.add_rule(rule).unwrap(); + } s } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index c76f583..7070a51 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -19,7 +19,6 @@ use crate::document::document::DocumentAccessors; use crate::document::element::ContainerElement; use crate::document::element::ElemKind; use crate::document::element::Element; -use crate::elements::customstyle::CustomStyleRule; use crate::elements::paragraph::Paragraph; use crate::lua::kernel::Kernel; use crate::lua::kernel::KernelHolder; @@ -186,7 +185,6 @@ impl<'a, 'b> ParserState<'a, 'b> { .for_each(|(rule, (matched_at, match_data))| { // Don't upate if not stepped over yet if *matched_at > cursor.pos { - // TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule return; } @@ -369,7 +367,7 @@ pub trait Parser { /// # Warning /// /// This method must not be called if a [`ParserState`] for this parser exists. - fn add_rule(&mut self, rule: Box, after: Option<&'static str>) -> Result<(), String> { + fn add_rule(&mut self, rule: Box) -> Result<(), String> { if let Some(_) = self .rules() .iter() @@ -381,23 +379,7 @@ pub trait Parser { )); } - // Try to insert after - if let Some(after) = after { - let index = self - .rules() - .iter() - .enumerate() - .find(|(_, rule)| rule.name() == after) - .map(|(idx, _)| idx); - - if let Some(index) = index { - self.rules_mut().insert(index, rule); - } else { - return Err(format!("Unable to find rule `{after}` to insert after")); - } - } else { - self.rules_mut().push(rule); - } + self.rules_mut().push(rule); Ok(()) } diff --git a/src/parser/rule.rs b/src/parser/rule.rs index aeca59d..5085b30 100644 --- a/src/parser/rule.rs +++ b/src/parser/rule.rs @@ -12,12 +12,66 @@ use mlua::Function; use mlua::Lua; use std::any::Any; +use std::collections::HashMap; use std::ops::Range; use std::rc::Rc; +macro_rules! create_registry { + ( $($construct:expr),+ $(,)? ) => {{ + let mut map = HashMap::new(); + $( + let boxed = Box::new($construct) as Box; + map.insert(boxed.name(), boxed); + )+ + map + }}; +} + +/// Gets the list of all rules exported with the [`auto_registry`] proc macro. +/// Rules are sorted according to topological order using the [`Rule::previous`] method. +#[auto_registry::generate_registry(registry = "rules", target = make_rules, return_type = HashMap<&'static str, Box>, maker = create_registry)] +pub fn get_rule_registry() -> Vec> { + fn cmp( + map: &HashMap<&'static str, Box>, + lname: &'static str, + rname: &'static str, + ) -> std::cmp::Ordering { + let l = map.get(lname).unwrap(); + let r = map.get(rname).unwrap(); + if l.previous() == Some(r.name()) { + std::cmp::Ordering::Greater + } else if r.previous() == Some(l.name()) { + std::cmp::Ordering::Less + } else if l.previous().is_some() && r.previous().is_none() { + std::cmp::Ordering::Greater + } else if r.previous().is_some() && l.previous().is_none() { + std::cmp::Ordering::Less + } else if let (Some(pl), Some(pr)) = (l.previous(), r.previous()) { + cmp(map, pl, pr) + } else { + std::cmp::Ordering::Equal + } + } + let mut map = make_rules(); + let mut sorted_keys = map.iter().map(|(key, _)| *key).collect::>(); + sorted_keys.sort_by(|l, r| cmp(&map, l, r)); + + let mut owned = Vec::with_capacity(sorted_keys.len()); + for key in sorted_keys { + let rule = map.remove(key).unwrap(); + owned.push(rule); + } + + owned +} + pub trait Rule: Downcast { - /// Returns rule's name + /// The rule name fn name(&self) -> &'static str; + + /// The name of the rule that should come before this one + fn previous(&self) -> Option<&'static str>; + /// Finds the next match starting from [`cursor`] fn next_match(&self, state: &ParserState, cursor: &Cursor) -> Option<(usize, Box)>; /// Callback when rule matches @@ -47,8 +101,12 @@ impl core::fmt::Debug for dyn Rule { } pub trait RegexRule { + /// The rule name fn name(&self) -> &'static str; + /// The name of the rule that should come before this one + fn previous(&self) -> Option<&'static str>; + /// Returns the rule's regexes fn regexes(&self) -> &[regex::Regex]; @@ -69,6 +127,7 @@ pub trait RegexRule { impl Rule for T { fn name(&self) -> &'static str { RegexRule::name(self) } + fn previous(&self) -> Option<&'static str> { RegexRule::previous(self) } /// Finds the next match starting from [`cursor`] fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box)> { @@ -120,3 +179,41 @@ impl Rule for T { fn register_layouts(&self, holder: &mut LayoutHolder) { self.register_layouts(holder); } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn registry() { + let rules = get_rule_registry(); + let names: Vec<&'static str> = rules.iter().map(|rule| rule.name()).collect(); + + assert_eq!( + names, + vec![ + "Comment", + "Paragraph", + "Import", + "Script", + "Element Style", + "Variable", + "Variable Substitution", + "Raw", + "List", + "Code", + "Tex", + "Graph", + "Media", + "Layout", + "Style", + "Custom Style", + "Section", + "Link", + "Text", + "Reference", + ] + ); + } +}