From 55c65fe30e7a92a4e093556baffac953c0cb9f55 Mon Sep 17 00:00:00 2001 From: ef3d0c3e Date: Fri, 26 Jul 2024 11:21:00 +0200 Subject: [PATCH] Partial References --- src/compiler/compiler.rs | 154 +++++++++++++---------- src/document/document.rs | 167 ++++++++++++++----------- src/document/element.rs | 117 +++++++++++------ src/elements/media.rs | 102 ++++++++++----- src/elements/section.rs | 264 ++++++++++++++++++++++----------------- src/main.rs | 19 ++- src/parser/langparser.rs | 5 + 7 files changed, 500 insertions(+), 328 deletions(-) diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 2ba9480..77adcbc 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -1,132 +1,155 @@ -use std::{cell::{RefCell, RefMut}, rc::Rc}; - +use std::cell::RefCell; +use std::cell::RefMut; +use std::collections::HashMap; +use std::rc::Rc; use rusqlite::Connection; -use crate::document::{document::Document, variable::Variable}; +use crate::document::document::Document; +use crate::document::document::ElemReference; +use crate::document::element::ReferenceableElement; +use crate::document::variable::Variable; #[derive(Clone, Copy)] -pub enum Target -{ +pub enum Target { HTML, LATEX, } -pub struct Compiler -{ +pub struct Compiler { target: Target, cache: Option>, + reference_count: RefCell>>, } -impl Compiler -{ +impl Compiler { pub fn new(target: Target, db_path: Option) -> Self { - let cache = match db_path - { + let cache = match db_path { None => None, - Some(path) => { - match Connection::open(path) - { - Err(e) => panic!("Cannot connect to database: {e}"), - Ok(con) => Some(con), - } - } + Some(path) => match Connection::open(path) { + Err(e) => panic!("Cannot connect to database: {e}"), + Ok(con) => Some(con), + }, }; Self { target, cache: cache.map(|con| RefCell::new(con)), + reference_count: RefCell::new(HashMap::new()), } } - pub fn target(&self) -> Target - { - self.target + /// Inserts or get a reference id for the compiled document + /// + /// # Parameters + /// - [`reference`] The reference to get or insert + pub fn reference_id<'a>(&self, document: &'a dyn Document, reference: ElemReference) -> usize { + let mut borrow = self.reference_count.borrow_mut(); + let reference = document.get_from_reference(&reference).unwrap(); + let refkey = reference.refcount_key(); + let refname = reference.reference_name().unwrap(); + + let map = match borrow.get_mut(refkey) { + Some(map) => map, + None => { + borrow.insert(refkey.to_string(), HashMap::new()); + borrow.get_mut(refkey).unwrap() + } + }; + + if let Some(elem) = map.get(refname) { + *elem + } else { + // Insert new ref + let index = map + .iter() + .fold(0, |max, (name, value)| std::cmp::max(max, *value)); + map.insert(refname.clone(), index + 1); + index + 1 + } } - pub fn cache(&self) -> Option> - { - self.cache - .as_ref() - .map(RefCell::borrow_mut) + pub fn target(&self) -> Target { self.target } + + pub fn cache(&self) -> Option> { + self.cache.as_ref().map(RefCell::borrow_mut) } pub fn sanitize>(&self, str: S) -> String { - match self.target - { - Target::HTML => str.as_ref() + match self.target { + Target::HTML => str + .as_ref() .replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("\"", """), - _ => todo!("Sanitize not implemented") + _ => todo!("Sanitize not implemented"), } } - pub fn header(&self, document: &dyn Document) -> String - { - pub fn get_variable_or_error(document: &dyn Document, var_name: &'static str) -> Option> - { - document.get_variable(var_name) + pub fn header(&self, document: &dyn Document) -> String { + pub fn get_variable_or_error( + document: &dyn Document, + var_name: &'static str, + ) -> Option> { + document + .get_variable(var_name) .and_then(|var| Some(var)) .or_else(|| { - println!("Missing variable `{var_name}` in {}", document.source().name()); + println!( + "Missing variable `{var_name}` in {}", + document.source().name() + ); None }) } let mut result = String::new(); - match self.target() - { + match self.target() { Target::HTML => { result += ""; result += ""; - if let Some(page_title) = get_variable_or_error(document, "html.page_title") - { - result += format!("{}", self.sanitize(page_title.to_string())).as_str(); + if let Some(page_title) = get_variable_or_error(document, "html.page_title") { + result += format!("{}", self.sanitize(page_title.to_string())) + .as_str(); } - if let Some(css) = document.get_variable("html.css") - { - result += format!("", self.sanitize(css.to_string())).as_str(); + if let Some(css) = document.get_variable("html.css") { + result += format!( + "", + self.sanitize(css.to_string()) + ) + .as_str(); } result += ""; // TODO: TOC // TODO: Author, Date, Title, Div - }, - Target::LATEX => { - - }, + } + Target::LATEX => {} } result } - pub fn footer(&self, _document: &dyn Document) -> String - { + pub fn footer(&self, _document: &dyn Document) -> String { let mut result = String::new(); - match self.target() - { + match self.target() { Target::HTML => { result += ""; - }, - Target::LATEX => { - - }, + } + Target::LATEX => {} } result } - - pub fn compile(&self, document: &dyn Document) -> String - { + + pub fn compile(&self, document: &dyn Document) -> String { let mut out = String::new(); let borrow = document.content().borrow(); // Header out += self.header(document).as_str(); - + // Body - for i in 0 .. borrow.len() - { + for i in 0..borrow.len() { let elem = &borrow[i]; //let prev = match i //{ @@ -135,13 +158,12 @@ impl Compiler //}; //let next = borrow.get(i+1); - match elem.compile(self, document) - { + match elem.compile(self, document) { Ok(result) => { //println!("Elem: {}\nCompiled to: {result}", elem.to_string()); out.push_str(result.as_str()) - }, - Err(err) => println!("Unable to compile element: {err}\n{}", elem.to_string()) + } + Err(err) => println!("Unable to compile element: {err}\n{}", elem.to_string()), } } diff --git a/src/document/document.rs b/src/document/document.rs index 60282a1..6bbb1cd 100644 --- a/src/document/document.rs +++ b/src/document/document.rs @@ -7,16 +7,22 @@ use std::rc::Rc; use crate::parser::source::Source; use super::element::Element; +use super::element::ReferenceableElement; use super::variable::Variable; -// TODO: Referenceable rework -// Usize based referencing is not an acceptable method -// if we want to support deltas for the lsp +#[derive(Debug, Clone, Copy)] +pub enum ElemReference { + Direct(usize), + + // Reference nested inside another element, e.g [`Paragraph`] or [`Media`] + Nested(usize, usize), +} + #[derive(Debug)] pub struct Scope { /// List of all referenceable elements in current scope. /// All elements in this should return a non empty - pub referenceable: HashMap, + pub referenceable: HashMap, pub variables: HashMap>, } @@ -32,12 +38,16 @@ impl Scope { match merge_as.is_empty() { true => { // References - self.referenceable.extend( - other - .referenceable - .drain() - .map(|(name, idx)| (name, idx + ref_offset)), - ); + self.referenceable.extend(other.referenceable.drain().map( + |(name, idx)| match idx { + ElemReference::Direct(index) => { + (name, ElemReference::Direct(index + ref_offset)) + } + ElemReference::Nested(index, sub_index) => { + (name, ElemReference::Nested(index + ref_offset, sub_index)) + } + }, + )); // Variables self.variables @@ -45,12 +55,18 @@ impl Scope { } false => { // References - self.referenceable.extend( - other - .referenceable - .drain() - .map(|(name, idx)| (format!("{merge_as}.{name}"), idx + ref_offset)), - ); + self.referenceable.extend(other.referenceable.drain().map( + |(name, idx)| match idx { + ElemReference::Direct(index) => ( + format!("{merge_as}.{name}"), + ElemReference::Direct(index + ref_offset), + ), + ElemReference::Nested(index, sub_index) => ( + format!("{merge_as}.{name}"), + ElemReference::Nested(index + ref_offset, sub_index), + ), + }, + )); // Variables self.variables.extend( @@ -80,44 +96,41 @@ pub trait Document<'a>: core::fmt::Debug { /// Pushes a new element into the document's content fn push(&self, elem: Box) { - // TODO: RefTable + if let Some(refname) = elem + .as_referenceable() + .and_then(|reference| reference.reference_name()) + { + self.scope().borrow_mut().referenceable.insert( + refname.clone(), + ElemReference::Direct(self.content().borrow().len()), + ); + } else if let Some(container) = self + .content() + .borrow() + .last() + .and_then(|elem| elem.as_container()) + { + // This is a hack that works thanks to the fact that at document end, a [`DocumentEnd`] elem is pushed + container + .contained() + .iter() + .enumerate() + .for_each(|(sub_idx, elem)| { + if let Some(refname) = elem + .as_referenceable() + .and_then(|reference| reference.reference_name()) + { + self.scope().borrow_mut().referenceable.insert( + refname.clone(), + ElemReference::Nested(self.content().borrow().len() - 1, sub_idx), + ); + } + }); + } self.content().borrow_mut().push(elem); } - /* - fn last_element(&'a self, recurse: bool) -> Option> - { - let elem = Ref::filter_map(self.content().borrow(), - |content| content.last() - .and_then(|last| last.downcast_ref::()) - ).ok(); - - - if elem.is_some() || !recurse { return elem } - - match self.parent() - { - None => None, - Some(parent) => parent.last_element(true), - } - } - - fn last_element_mut(&'a self, recurse: bool) -> Option> - { - let elem = RefMut::filter_map(self.content().borrow_mut(), - |content| content.last_mut()).ok(); - - if elem.is_some() || !recurse { return elem } - - match self.parent() - { - None => None, - Some(parent) => parent.last_element_mut(true), - } - } - */ - fn add_variable(&self, variable: Rc) { self.scope() .borrow_mut() @@ -141,27 +154,11 @@ pub trait Document<'a>: core::fmt::Debug { } } - /* - fn remove_variable(&self, name: &str) -> Option> - { - match self.scope().borrow_mut().variables.remove(name) - { - Some(variable) => { - return Some(variable.clone()); - }, - - // Continue search recursively - None => match self.parent() { - Some(parent) => return parent.remove_variable(name), - - // Not found - None => return None, - } - } - } - */ - /// Merges [`other`] into [`self`] + /// + /// # Parameters + /// + /// If [`merge_as`] is None, references and variables from the other document are not merged into self fn merge( &self, content: &RefCell>>, @@ -182,6 +179,36 @@ pub trait Document<'a>: core::fmt::Debug { .borrow_mut() .extend((content.borrow_mut()).drain(..).map(|value| value)); } + + fn get_reference(&self, refname: &str) -> Option { + self.scope() + .borrow() + .referenceable + .get(refname) + .and_then(|reference| Some(*reference)) + } + + fn get_from_reference( + &self, + reference: &ElemReference, + ) -> Option> { + match reference { + ElemReference::Direct(idx) => Ref::filter_map(self.content().borrow(), |content| { + content.get(*idx).and_then(|elem| elem.as_referenceable()) + }) + .ok(), + ElemReference::Nested(idx, sub_idx) => { + Ref::filter_map(self.content().borrow(), |content| { + content + .get(*idx) + .and_then(|elem| elem.as_container()) + .and_then(|container| container.contained().get(*sub_idx)) + .and_then(|elem| elem.as_referenceable()) + }) + .ok() + } + } + } } pub trait DocumentAccessors<'a> { diff --git a/src/document/element.rs b/src/document/element.rs index c12cf98..d51c0c8 100644 --- a/src/document/element.rs +++ b/src/document/element.rs @@ -1,65 +1,108 @@ use std::str::FromStr; -use downcast_rs::{impl_downcast, Downcast}; -use crate::{compiler::compiler::Compiler, parser::source::Token}; +use crate::compiler::compiler::Compiler; +use crate::parser::source::Token; +use downcast_rs::impl_downcast; +use downcast_rs::Downcast; use super::document::Document; #[derive(Debug, PartialEq, Eq, Clone)] pub enum ElemKind { - /// An invisible element (i.e comment) - Invisible, - /// Special elements don't trigger special formatting events - Special, - /// Inline elements don't break paragraphing - Inline, - /// Block elements are outside of paragraphs - Block, + /// An invisible element (i.e comment) + Invisible, + /// Special elements don't trigger special formatting events + Special, + /// Inline elements don't break paragraphing + Inline, + /// Block elements are outside of paragraphs + Block, } impl FromStr for ElemKind { - type Err = String; + type Err = String; - fn from_str(s: &str) -> Result { - match s - { + fn from_str(s: &str) -> Result { + match s { "invisible" => Ok(ElemKind::Invisible), - "special" => Ok(ElemKind::Special), - "inline" => Ok(ElemKind::Inline), - "block" => Ok(ElemKind::Block), - _ => Err(format!("Unknown ElemKind: {s}")) + "special" => Ok(ElemKind::Special), + "inline" => Ok(ElemKind::Inline), + "block" => Ok(ElemKind::Block), + _ => Err(format!("Unknown ElemKind: {s}")), } - } + } } -pub trait Element: Downcast -{ - /// Gets the element defined location i.e token without filename - fn location(&self) -> &Token; +pub trait Element: Downcast { + /// Gets the element defined location i.e token without filename + fn location(&self) -> &Token; - fn kind(&self) -> ElemKind; + fn kind(&self) -> ElemKind; - /// Get the element's name + /// Get the element's name fn element_name(&self) -> &'static str; - /// Outputs element to string for debug purposes - fn to_string(&self) -> String; + /// Outputs element to string for debug purposes + fn to_string(&self) -> String; - fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { None } + /// Gets the element as a referenceable i.e an element that can be referenced + fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { None } - /// Compiles element - fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result; + /// Gets the element as a container containing other elements + fn as_container(&self) -> Option<&dyn ContainerElement> { None } + + /// Compiles element + fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result; } impl_downcast!(Element); -impl core::fmt::Debug for dyn Element -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.to_string()) - } +impl core::fmt::Debug for dyn Element { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_string()) + } } -pub trait ReferenceableElement : Element { - /// Reference name +pub trait ReferenceableElement: Element { + /// Reference name fn reference_name(&self) -> Option<&String>; + + /// Key for refcounting + fn refcount_key(&self) -> &'static str; +} + +impl core::fmt::Debug for dyn ReferenceableElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +pub trait ContainerElement: Element { + /// Gets the contained elements + fn contained(&self) -> &Vec>; + + /// Adds an element to the container + fn push(&mut self, elem: Box) -> Result<(), String>; +} + +impl core::fmt::Debug for dyn ContainerElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +#[derive(Debug)] +pub struct DocumentEnd(pub Token); + +impl Element for DocumentEnd { + fn location(&self) -> &Token { &self.0 } + + fn kind(&self) -> ElemKind { ElemKind::Invisible } + + fn element_name(&self) -> &'static str { "Document End" } + + fn to_string(&self) -> String { format!("{self:#?}") } + + fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result { + Ok(String::new()) + } } diff --git a/src/elements/media.rs b/src/elements/media.rs index 0dfe144..1923cb4 100644 --- a/src/elements/media.rs +++ b/src/elements/media.rs @@ -16,8 +16,10 @@ use crate::compiler::compiler::Compiler; use crate::compiler::compiler::Target; use crate::document::document::Document; use crate::document::document::DocumentAccessors; +use crate::document::element::ContainerElement; use crate::document::element::ElemKind; use crate::document::element::Element; +use crate::document::element::ReferenceableElement; use crate::document::references::validate_refname; use crate::parser::parser::ReportColors; use crate::parser::rule::RegexRule; @@ -54,36 +56,22 @@ impl FromStr for MediaType { } #[derive(Debug)] -struct MediaGroup { +struct Media { pub(self) location: Token, - pub(self) media: Vec, + pub(self) media: Vec>, } -impl MediaGroup { - fn push(&mut self, media: Media) -> Result<(), String> { - if self.location.source() != media.location.source() { - return Err(format!( - "Attempted to insert media from {} into MediaGroup from {}", - self.location.source(), - media.location.source() - )); - } - - self.location.range = self.location.start()..media.location.end(); - self.media.push(media); - Ok(()) - } -} - -impl Element for MediaGroup { +impl Element for Media { fn location(&self) -> &Token { &self.location } fn kind(&self) -> ElemKind { ElemKind::Block } - fn element_name(&self) -> &'static str { "Media Group" } + fn element_name(&self) -> &'static str { "Media" } fn to_string(&self) -> String { format!("{self:#?}") } + fn as_container(&self) -> Option<&dyn ContainerElement> { Some(self) } + fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result { match compiler.target() { Target::HTML => { @@ -105,8 +93,30 @@ impl Element for MediaGroup { } } +impl ContainerElement for Media { + fn contained(&self) -> &Vec> { &self.media } + + fn push(&mut self, elem: Box) -> Result<(), String> { + let medium = match elem.downcast_ref::() { + Some(medium) => medium, + None => return Err("Attempted to insert invalid element into Media".to_string()), + }; + if self.location.source() != medium.location.source() { + return Err(format!( + "Attempted to insert medium from {} into medium from {}", + self.location.source(), + medium.location.source() + )); + } + + self.location.range = self.location.start()..medium.location.end(); + self.media.push(elem); + Ok(()) + } +} + #[derive(Debug)] -struct Media { +struct Medium { pub(self) location: Token, pub(self) reference: String, pub(self) uri: String, @@ -116,15 +126,17 @@ struct Media { pub(self) description: Option, } -impl Element for Media { +impl Element for Medium { fn location(&self) -> &Token { &self.location } fn kind(&self) -> ElemKind { ElemKind::Block } - fn element_name(&self) -> &'static str { "Media" } + fn element_name(&self) -> &'static str { "Medium" } fn to_string(&self) -> String { format!("{self:#?}") } + fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) } + fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result { match compiler.target() { Target::HTML => { @@ -142,7 +154,19 @@ impl Element for Media { MediaType::VIDEO => todo!(), MediaType::AUDIO => todo!(), } - result.push_str(format!(r#"

{}

"#, "TODO").as_str()); + + let caption = self + .caption + .as_ref() + .and_then(|cap| Some(format!(" {}", compiler.sanitize(cap.as_str())))) + .unwrap_or(String::new()); + + // Reference + let elemref = document.get_reference(self.reference.as_str()).unwrap(); + let refcount = compiler.reference_id(document, elemref); + result.push_str( + format!(r#"

({refcount}){caption}

"#).as_str(), + ); if let Some(paragraph) = self.description.as_ref() { match paragraph.compile(compiler, document) { Ok(res) => result.push_str(res.as_str()), @@ -158,6 +182,12 @@ impl Element for Media { } } +impl ReferenceableElement for Medium { + fn reference_name(&self) -> Option<&String> { Some(&self.reference) } + + fn refcount_key(&self) -> &'static str { "medium" } +} + pub struct MediaRule { re: [Regex; 1], properties: PropertyParser, @@ -178,6 +208,10 @@ impl MediaRule { "width".to_string(), Property::new(false, "Override for the media width".to_string(), None), ); + props.insert( + "caption".to_string(), + Property::new(false, "Medium caption".to_string(), None), + ); Self { re: [RegexBuilder::new( r"^!\[(.*)\]\(((?:\\.|[^\\\\])*?)\)(?:\[((?:\\.|[^\\\\])*?)\])?((?:\\(?:.|\n)|[^\\\\])*?$)?", @@ -366,6 +400,13 @@ impl RegexRule for MediaRule { .ok() .and_then(|(_, s)| Some(s)); + let caption = properties + .get("caption", |_, value| -> Result { + Ok(value.clone()) + }) + .ok() + .and_then(|(_, value)| Some(value)); + let description = match matches.get(4) { Some(content) => { let source = Rc::new(VirtualSource::new( @@ -399,31 +440,30 @@ impl RegexRule for MediaRule { None => panic!("Unknown error"), }; - // TODO: caption - let mut group = match document.last_element_mut::() { + let mut group = match document.last_element_mut::() { Some(group) => group, None => { parser.push( document, - Box::new(MediaGroup { + Box::new(Media { location: token.clone(), media: vec![], }), ); - document.last_element_mut::().unwrap() + document.last_element_mut::().unwrap() } }; - if let Err(err) = group.push(Media { + if let Err(err) = group.push(Box::new(Medium { location: token.clone(), reference: refname, uri, media_type, width, - caption: None, + caption, description, - }) { + })) { reports.push( Report::build(ReportKind::Error, token.source(), token.start()) .with_message("Invalid Media") diff --git a/src/elements/section.rs b/src/elements/section.rs index 9af30a6..3b9dbe3 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -1,42 +1,57 @@ -use mlua::{Error::BadArgument, Function, Lua}; +use crate::compiler::compiler::Compiler; +use crate::compiler::compiler::Target; +use crate::document::document::Document; +use crate::document::element::ElemKind; +use crate::document::element::Element; +use crate::document::element::ReferenceableElement; +use crate::lua::kernel::CTX; +use crate::parser::parser::Parser; +use crate::parser::rule::RegexRule; +use crate::parser::source::Source; +use crate::parser::source::Token; +use ariadne::Fmt; +use ariadne::Label; +use ariadne::Report; +use ariadne::ReportKind; +use mlua::Error::BadArgument; +use mlua::Function; +use mlua::Lua; use regex::Regex; -use crate::{compiler::compiler::Target, document::document::Document, lua::kernel::CTX, parser::{parser::Parser, rule::RegexRule, source::{Source, Token}}}; -use ariadne::{Report, Fmt, Label, ReportKind}; -use crate::{compiler::compiler::Compiler, document::element::{ElemKind, Element, ReferenceableElement}}; -use std::{ops::Range, rc::Rc, sync::Arc}; +use std::ops::Range; +use std::rc::Rc; +use std::sync::Arc; #[derive(Debug)] pub struct Section { - pub(self) location: Token, - pub(self) title: String, // Section title - pub(self) depth: usize, // Section depth - pub(self) kind: u8, // Section kind, e.g numbered, unnumbred, ... + pub(self) location: Token, + pub(self) title: String, // Section title + pub(self) depth: usize, // Section depth + pub(self) kind: u8, // Section kind, e.g numbered, unnumbred, ... pub(self) reference: Option, // Section reference name } -impl Element for Section -{ - fn location(&self) -> &Token { &self.location } - fn kind(&self) -> ElemKind { ElemKind::Block } - fn element_name(&self) -> &'static str { "Section" } - fn to_string(&self) -> String { format!("{self:#?}") } - fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) } - fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result { - match compiler.target() - { - Target::HTML => { - Ok(format!("{1}", - self.depth, - compiler.sanitize(self.title.as_str()))) - } - Target::LATEX => Err("Unimplemented compiler".to_string()) - } - } +impl Element for Section { + fn location(&self) -> &Token { &self.location } + fn kind(&self) -> ElemKind { ElemKind::Block } + fn element_name(&self) -> &'static str { "Section" } + fn to_string(&self) -> String { format!("{self:#?}") } + fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) } + fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result { + match compiler.target() { + Target::HTML => Ok(format!( + "{1}", + self.depth, + compiler.sanitize(self.title.as_str()) + )), + Target::LATEX => Err("Unimplemented compiler".to_string()), + } + } } -impl ReferenceableElement for Section -{ - fn reference_name(&self) -> Option<&String> { self.reference.as_ref() } +impl ReferenceableElement for Section { + fn reference_name(&self) -> Option<&String> { self.reference.as_ref() } + + fn refcount_key(&self) -> &'static str { "section" } } pub struct SectionRule { @@ -45,15 +60,16 @@ pub struct SectionRule { impl SectionRule { pub fn new() -> Self { - Self { re: [Regex::new(r"(?:^|\n)(#{1,})(?:\{(.*)\})?((\*|\+){1,})?(.*)").unwrap()] } + Self { + re: [Regex::new(r"(?:^|\n)(#{1,})(?:\{(.*)\})?((\*|\+){1,})?(.*)").unwrap()], + } } } -pub mod section_kind -{ - pub const NONE : u8 = 0x00; - pub const NO_TOC : u8 = 0x01; - pub const NO_NUMBER : u8 = 0x02; +pub mod section_kind { + pub const NONE: u8 = 0x00; + pub const NO_TOC: u8 = 0x01; + pub const NO_NUMBER: u8 = 0x02; } impl RegexRule for SectionRule { @@ -61,11 +77,16 @@ impl RegexRule for SectionRule { fn regexes(&self) -> &[Regex] { &self.re } - fn on_regex_match(&self, _: usize, parser: &dyn Parser, document: &dyn Document, token: Token, matches: regex::Captures) -> Vec, Range)>> - { + fn on_regex_match( + &self, + _: usize, + parser: &dyn Parser, + document: &dyn Document, + token: Token, + matches: regex::Captures, + ) -> Vec, Range)>> { let mut result = vec![]; - let section_depth = match matches.get(1) - { + let section_depth = match matches.get(1) { Some(depth) => { if depth.len() > 6 { result.push( @@ -78,16 +99,17 @@ impl RegexRule for SectionRule { 6.fg(parser.colors().info))) .with_color(parser.colors().error)) .finish()); - return result; + return result; } depth.len() - }, + } _ => panic!("Empty section depth"), }; // [Optional] Reference name - let section_refname = matches.get(2).map_or_else(|| None, + let section_refname = matches.get(2).map_or_else( + || None, |refname| { /* TODO: Wait for reference rework // Check for duplicate reference @@ -105,29 +127,28 @@ impl RegexRule for SectionRule { refname.as_str().fg(parser.colors().highlight), reference.element_name().fg(parser.colors().highlight))) .with_color(parser.colors().warning)) - .with_label( - Label::new((ref_doc.source(), reference.location().start()+1..reference.location().end() )) - .with_message(format!("`{}` previously defined here", - refname.as_str().fg(parser.colors().highlight))) + .with_label( + Label::new((ref_doc.source(), reference.location().start()+1..reference.location().end() )) + .with_message(format!("`{}` previously defined here", + refname.as_str().fg(parser.colors().highlight))) .with_color(parser.colors().warning)) .with_note(format!("Previous reference was overwritten")) .finish()); } */ - Some(refname.as_str().to_string()) - }); + Some(refname.as_str().to_string()) + }, + ); // Section kind - let section_kind = match matches.get(3) - { - Some(kind) => { - match kind.as_str() { - "*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC, - "*" => section_kind::NO_NUMBER, - "+" => section_kind::NO_TOC, - "" => section_kind::NONE, - _ => { - result.push( + let section_kind = match matches.get(3) { + Some(kind) => match kind.as_str() { + "*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC, + "*" => section_kind::NO_NUMBER, + "+" => section_kind::NO_TOC, + "" => section_kind::NONE, + _ => { + result.push( Report::build(ReportKind::Error, token.source(), kind.start()) .with_message("Invalid section numbering kind") .with_label( @@ -139,38 +160,40 @@ impl RegexRule for SectionRule { .with_color(parser.colors().error)) .with_help(format!("Leave empty for a numbered listed section")) .finish()); - return result; - } + return result; } - } + }, _ => section_kind::NONE, }; // Spacing + Section name - let section_name = match matches.get(5) - { + let section_name = match matches.get(5) { Some(name) => { - let split = name.as_str().chars() + let split = name + .as_str() + .chars() .position(|c| !c.is_whitespace()) .unwrap_or(0); let section_name = &name.as_str()[split..]; - if section_name.is_empty() // No name + if section_name.is_empty() + // No name { result.push( Report::build(ReportKind::Error, token.source(), name.start()) - .with_message("Missing section name") - .with_label( - Label::new((token.source(), name.range())) - .with_message("Sections require a name before line end") - .with_color(parser.colors().error)) - .finish()); + .with_message("Missing section name") + .with_label( + Label::new((token.source(), name.range())) + .with_message("Sections require a name before line end") + .with_color(parser.colors().error), + ) + .finish(), + ); return result; } // No spacing - if split == 0 - { + if split == 0 { result.push( Report::build(ReportKind::Warning, token.source(), name.start()) .with_message("Missing section spacing") @@ -183,57 +206,70 @@ impl RegexRule for SectionRule { return result; } - section_name.to_string() - }, - _ => panic!("Empty section name") + } + _ => panic!("Empty section name"), }; - parser.push(document, Box::new( - Section { + parser.push( + document, + Box::new(Section { location: token.clone(), - title: section_name, - depth: section_depth, - kind: section_kind, - reference: section_refname - } - )); + title: section_name, + depth: section_depth, + kind: section_kind, + reference: section_refname, + }), + ); - return result; + return result; } - fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> - { + fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { let mut bindings = vec![]; - bindings.push(("push".to_string(), lua.create_function( - |_, (title, depth, kind, reference) : (String, usize, String, Option)| { - let kind = match kind.as_str() { - "*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC, - "*" => section_kind::NO_NUMBER, - "+" => section_kind::NO_TOC, - "" => section_kind::NONE, - _ => return Err(BadArgument { - to: Some("push".to_string()), - pos: 3, - name: Some("kind".to_string()), - cause: Arc::new(mlua::Error::external( - format!("Unknown section kind specified")))}) - }; + bindings.push(( + "push".to_string(), + lua.create_function( + |_, (title, depth, kind, reference): (String, usize, String, Option)| { + let kind = match kind.as_str() { + "*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC, + "*" => section_kind::NO_NUMBER, + "+" => section_kind::NO_TOC, + "" => section_kind::NONE, + _ => { + return Err(BadArgument { + to: Some("push".to_string()), + pos: 3, + name: Some("kind".to_string()), + cause: Arc::new(mlua::Error::external(format!( + "Unknown section kind specified" + ))), + }) + } + }; - CTX.with_borrow(|ctx| ctx.as_ref().map(|ctx| { - ctx.parser.push(ctx.document, Box::new(Section { - location: ctx.location.clone(), - title, - depth, - kind, - reference - })); - })); + CTX.with_borrow(|ctx| { + ctx.as_ref().map(|ctx| { + ctx.parser.push( + ctx.document, + Box::new(Section { + location: ctx.location.clone(), + title, + depth, + kind, + reference, + }), + ); + }) + }); + + Ok(()) + }, + ) + .unwrap(), + )); - Ok(()) - }).unwrap())); - bindings } } diff --git a/src/main.rs b/src/main.rs index b24efdd..77295e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,16 +85,15 @@ fn main() { println!("-- END AST DEBUGGING --"); } - // TODO - //if debug_opts.contains(&"ref".to_string()) - //{ - // println!("-- BEGIN REFERENCES DEBUGGING --"); - // let sc = doc.scope.borrow(); - // sc.referenceable.iter().for_each(|(name, pos)| { - // println!(" - {name}: `{:#?}`", doc.content.borrow()[*pos]); - // }); - // println!("-- END REFERENCES DEBUGGING --"); - //} + if debug_opts.contains(&"ref".to_string()) + { + println!("-- BEGIN REFERENCES DEBUGGING --"); + let sc = doc.scope().borrow(); + sc.referenceable.iter().for_each(|(name, reference)| { + println!(" - {name}: `{:#?}`", doc.get_from_reference(reference)); + }); + println!("-- END REFERENCES DEBUGGING --"); + } if debug_opts.contains(&"var".to_string()) { println!("-- BEGIN VARIABLES DEBUGGING --"); let sc = doc.scope().borrow(); diff --git a/src/parser/langparser.rs b/src/parser/langparser.rs index a4bed22..dddfac7 100644 --- a/src/parser/langparser.rs +++ b/src/parser/langparser.rs @@ -10,6 +10,7 @@ use ariadne::Report; use crate::document::document::Document; use crate::document::document::DocumentAccessors; +use crate::document::element::DocumentEnd; use crate::document::element::ElemKind; use crate::document::element::Element; use crate::document::langdocument::LangDocument; @@ -227,6 +228,10 @@ impl Parser for LangParser { .on_scope_end(self, &doc, super::state::Scope::DOCUMENT), ); + self.push(&doc, Box::new(DocumentEnd( + Token::new(doc.source().content().len()..doc.source().content().len(), doc.source()) + ))); + return Box::new(doc); }