Partial References

This commit is contained in:
ef3d0c3e 2024-07-26 11:21:00 +02:00
parent 8bd4f9716b
commit 55c65fe30e
7 changed files with 500 additions and 328 deletions

View file

@ -1,123 +1,147 @@
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<RefCell<Connection>>,
reference_count: RefCell<HashMap<String, HashMap<String, usize>>>,
}
impl Compiler
{
impl Compiler {
pub fn new(target: Target, db_path: Option<String>) -> 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<RefMut<'_, Connection>>
{
self.cache
.as_ref()
.map(RefCell::borrow_mut)
pub fn target(&self) -> Target { self.target }
pub fn cache(&self) -> Option<RefMut<'_, Connection>> {
self.cache.as_ref().map(RefCell::borrow_mut)
}
pub fn sanitize<S: AsRef<str>>(&self, str: S) -> String {
match self.target
{
Target::HTML => str.as_ref()
match self.target {
Target::HTML => str
.as_ref()
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;"),
_ => 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<Rc<dyn Variable>>
{
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<Rc<dyn Variable>> {
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 += "<!DOCTYPE HTML><html><head>";
result += "<meta charset=\"UTF-8\">";
if let Some(page_title) = get_variable_or_error(document, "html.page_title")
{
result += format!("<title>{}</title>", self.sanitize(page_title.to_string())).as_str();
if let Some(page_title) = get_variable_or_error(document, "html.page_title") {
result += format!("<title>{}</title>", self.sanitize(page_title.to_string()))
.as_str();
}
if let Some(css) = document.get_variable("html.css")
{
result += format!("<link rel=\"stylesheet\" href=\"{}\">", self.sanitize(css.to_string())).as_str();
if let Some(css) = document.get_variable("html.css") {
result += format!(
"<link rel=\"stylesheet\" href=\"{}\">",
self.sanitize(css.to_string())
)
.as_str();
}
result += "</head><body>";
// 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 += "</body></html>";
},
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();
@ -125,8 +149,7 @@ impl Compiler
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()),
}
}

View file

@ -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<String, usize>,
pub referenceable: HashMap<String, ElemReference>,
pub variables: HashMap<String, Rc<dyn Variable>>,
}
@ -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<dyn Element>) {
// 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<Ref<'_, dyn Element>>
{
let elem = Ref::filter_map(self.content().borrow(),
|content| content.last()
.and_then(|last| last.downcast_ref::<Element>())
).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<RefMut<'_, dyn Element>>
{
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<dyn Variable>) {
self.scope()
.borrow_mut()
@ -141,27 +154,11 @@ pub trait Document<'a>: core::fmt::Debug {
}
}
/*
fn remove_variable(&self, name: &str) -> Option<Rc<dyn Variable>>
{
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<Vec<Box<dyn Element>>>,
@ -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<ElemReference> {
self.scope()
.borrow()
.referenceable
.get(refname)
.and_then(|reference| Some(*reference))
}
fn get_from_reference(
&self,
reference: &ElemReference,
) -> Option<Ref<'_, dyn ReferenceableElement>> {
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> {

View file

@ -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<Self, Self::Err> {
match s
{
fn from_str(s: &str) -> Result<Self, Self::Err> {
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<String, String>;
/// 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<String, String>;
}
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<Box<dyn Element>>;
/// Adds an element to the container
fn push(&mut self, elem: Box<dyn Element>) -> 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<String, String> {
Ok(String::new())
}
}

View file

@ -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<Media>,
pub(self) media: Vec<Box<dyn Element>>,
}
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<String, String> {
match compiler.target() {
Target::HTML => {
@ -105,8 +93,30 @@ impl Element for MediaGroup {
}
}
impl ContainerElement for Media {
fn contained(&self) -> &Vec<Box<dyn Element>> { &self.media }
fn push(&mut self, elem: Box<dyn Element>) -> Result<(), String> {
let medium = match elem.downcast_ref::<Medium>() {
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<Paragraph>,
}
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<String, String> {
match compiler.target() {
Target::HTML => {
@ -142,7 +154,19 @@ impl Element for Media {
MediaType::VIDEO => todo!(),
MediaType::AUDIO => todo!(),
}
result.push_str(format!(r#"<p class="medium-refname">{}</p>"#, "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#"<p class="medium-refname">({refcount}){caption}</p>"#).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<String, ()> {
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::<MediaGroup>() {
let mut group = match document.last_element_mut::<Media>() {
Some(group) => group,
None => {
parser.push(
document,
Box::new(MediaGroup {
Box::new(Media {
location: token.clone(),
media: vec![],
}),
);
document.last_element_mut::<MediaGroup>().unwrap()
document.last_element_mut::<Media>().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")

View file

@ -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<String>, // 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<String, String> {
match compiler.target()
{
Target::HTML => {
Ok(format!("<h{0}>{1}</h{0}>",
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<String, String> {
match compiler.target() {
Target::HTML => Ok(format!(
"<h{0}>{1}</h{0}>",
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<Report<'_, (Rc<dyn Source>, Range<usize>)>>
{
fn on_regex_match(
&self,
_: usize,
parser: &dyn Parser,
document: &dyn Document,
token: Token,
matches: regex::Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
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,56 +206,69 @@ 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<String>)| {
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<String>)| {
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
}

View file

@ -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();

View file

@ -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);
}