Partial References
This commit is contained in:
parent
8bd4f9716b
commit
55c65fe30e
7 changed files with 500 additions and 328 deletions
|
@ -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 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)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Target
|
pub enum Target {
|
||||||
{
|
|
||||||
HTML,
|
HTML,
|
||||||
LATEX,
|
LATEX,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Compiler
|
pub struct Compiler {
|
||||||
{
|
|
||||||
target: Target,
|
target: Target,
|
||||||
cache: Option<RefCell<Connection>>,
|
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 {
|
pub fn new(target: Target, db_path: Option<String>) -> Self {
|
||||||
let cache = match db_path
|
let cache = match db_path {
|
||||||
{
|
|
||||||
None => None,
|
None => None,
|
||||||
Some(path) => {
|
Some(path) => match Connection::open(path) {
|
||||||
match Connection::open(path)
|
Err(e) => panic!("Cannot connect to database: {e}"),
|
||||||
{
|
Ok(con) => Some(con),
|
||||||
Err(e) => panic!("Cannot connect to database: {e}"),
|
},
|
||||||
Ok(con) => Some(con),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
target,
|
target,
|
||||||
cache: cache.map(|con| RefCell::new(con)),
|
cache: cache.map(|con| RefCell::new(con)),
|
||||||
|
reference_count: RefCell::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target(&self) -> Target
|
/// Inserts or get a reference id for the compiled document
|
||||||
{
|
///
|
||||||
self.target
|
/// # 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>>
|
pub fn target(&self) -> Target { self.target }
|
||||||
{
|
|
||||||
self.cache
|
pub fn cache(&self) -> Option<RefMut<'_, Connection>> {
|
||||||
.as_ref()
|
self.cache.as_ref().map(RefCell::borrow_mut)
|
||||||
.map(RefCell::borrow_mut)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sanitize<S: AsRef<str>>(&self, str: S) -> String {
|
pub fn sanitize<S: AsRef<str>>(&self, str: S) -> String {
|
||||||
match self.target
|
match self.target {
|
||||||
{
|
Target::HTML => str
|
||||||
Target::HTML => str.as_ref()
|
.as_ref()
|
||||||
.replace("&", "&")
|
.replace("&", "&")
|
||||||
.replace("<", "<")
|
.replace("<", "<")
|
||||||
.replace(">", ">")
|
.replace(">", ">")
|
||||||
.replace("\"", """),
|
.replace("\"", """),
|
||||||
_ => todo!("Sanitize not implemented")
|
_ => todo!("Sanitize not implemented"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header(&self, document: &dyn Document) -> String
|
pub fn header(&self, document: &dyn Document) -> String {
|
||||||
{
|
pub fn get_variable_or_error(
|
||||||
pub fn get_variable_or_error(document: &dyn Document, var_name: &'static str) -> Option<Rc<dyn Variable>>
|
document: &dyn Document,
|
||||||
{
|
var_name: &'static str,
|
||||||
document.get_variable(var_name)
|
) -> Option<Rc<dyn Variable>> {
|
||||||
|
document
|
||||||
|
.get_variable(var_name)
|
||||||
.and_then(|var| Some(var))
|
.and_then(|var| Some(var))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
println!("Missing variable `{var_name}` in {}", document.source().name());
|
println!(
|
||||||
|
"Missing variable `{var_name}` in {}",
|
||||||
|
document.source().name()
|
||||||
|
);
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
match self.target()
|
match self.target() {
|
||||||
{
|
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
result += "<!DOCTYPE HTML><html><head>";
|
result += "<!DOCTYPE HTML><html><head>";
|
||||||
result += "<meta charset=\"UTF-8\">";
|
result += "<meta charset=\"UTF-8\">";
|
||||||
if let Some(page_title) = get_variable_or_error(document, "html.page_title")
|
if let Some(page_title) = get_variable_or_error(document, "html.page_title") {
|
||||||
{
|
result += format!("<title>{}</title>", self.sanitize(page_title.to_string()))
|
||||||
result += format!("<title>{}</title>", self.sanitize(page_title.to_string())).as_str();
|
.as_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(css) = document.get_variable("html.css")
|
if let Some(css) = document.get_variable("html.css") {
|
||||||
{
|
result += format!(
|
||||||
result += format!("<link rel=\"stylesheet\" href=\"{}\">", self.sanitize(css.to_string())).as_str();
|
"<link rel=\"stylesheet\" href=\"{}\">",
|
||||||
|
self.sanitize(css.to_string())
|
||||||
|
)
|
||||||
|
.as_str();
|
||||||
}
|
}
|
||||||
result += "</head><body>";
|
result += "</head><body>";
|
||||||
|
|
||||||
// TODO: TOC
|
// TODO: TOC
|
||||||
// TODO: Author, Date, Title, Div
|
// TODO: Author, Date, Title, Div
|
||||||
},
|
}
|
||||||
Target::LATEX => {
|
Target::LATEX => {}
|
||||||
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn footer(&self, _document: &dyn Document) -> String
|
pub fn footer(&self, _document: &dyn Document) -> String {
|
||||||
{
|
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
match self.target()
|
match self.target() {
|
||||||
{
|
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
result += "</body></html>";
|
result += "</body></html>";
|
||||||
},
|
}
|
||||||
Target::LATEX => {
|
Target::LATEX => {}
|
||||||
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&self, document: &dyn Document) -> String
|
pub fn compile(&self, document: &dyn Document) -> String {
|
||||||
{
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
let borrow = document.content().borrow();
|
let borrow = document.content().borrow();
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
out += self.header(document).as_str();
|
out += self.header(document).as_str();
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
for i in 0 .. borrow.len()
|
for i in 0..borrow.len() {
|
||||||
{
|
|
||||||
let elem = &borrow[i];
|
let elem = &borrow[i];
|
||||||
//let prev = match i
|
//let prev = match i
|
||||||
//{
|
//{
|
||||||
|
@ -135,13 +158,12 @@ impl Compiler
|
||||||
//};
|
//};
|
||||||
//let next = borrow.get(i+1);
|
//let next = borrow.get(i+1);
|
||||||
|
|
||||||
match elem.compile(self, document)
|
match elem.compile(self, document) {
|
||||||
{
|
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
//println!("Elem: {}\nCompiled to: {result}", elem.to_string());
|
//println!("Elem: {}\nCompiled to: {result}", elem.to_string());
|
||||||
out.push_str(result.as_str())
|
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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,22 @@ use std::rc::Rc;
|
||||||
use crate::parser::source::Source;
|
use crate::parser::source::Source;
|
||||||
|
|
||||||
use super::element::Element;
|
use super::element::Element;
|
||||||
|
use super::element::ReferenceableElement;
|
||||||
use super::variable::Variable;
|
use super::variable::Variable;
|
||||||
|
|
||||||
// TODO: Referenceable rework
|
#[derive(Debug, Clone, Copy)]
|
||||||
// Usize based referencing is not an acceptable method
|
pub enum ElemReference {
|
||||||
// if we want to support deltas for the lsp
|
Direct(usize),
|
||||||
|
|
||||||
|
// Reference nested inside another element, e.g [`Paragraph`] or [`Media`]
|
||||||
|
Nested(usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
/// List of all referenceable elements in current scope.
|
/// List of all referenceable elements in current scope.
|
||||||
/// All elements in this should return a non empty
|
/// 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>>,
|
pub variables: HashMap<String, Rc<dyn Variable>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,12 +38,16 @@ impl Scope {
|
||||||
match merge_as.is_empty() {
|
match merge_as.is_empty() {
|
||||||
true => {
|
true => {
|
||||||
// References
|
// References
|
||||||
self.referenceable.extend(
|
self.referenceable.extend(other.referenceable.drain().map(
|
||||||
other
|
|(name, idx)| match idx {
|
||||||
.referenceable
|
ElemReference::Direct(index) => {
|
||||||
.drain()
|
(name, ElemReference::Direct(index + ref_offset))
|
||||||
.map(|(name, idx)| (name, idx + ref_offset)),
|
}
|
||||||
);
|
ElemReference::Nested(index, sub_index) => {
|
||||||
|
(name, ElemReference::Nested(index + ref_offset, sub_index))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
self.variables
|
self.variables
|
||||||
|
@ -45,12 +55,18 @@ impl Scope {
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
// References
|
// References
|
||||||
self.referenceable.extend(
|
self.referenceable.extend(other.referenceable.drain().map(
|
||||||
other
|
|(name, idx)| match idx {
|
||||||
.referenceable
|
ElemReference::Direct(index) => (
|
||||||
.drain()
|
format!("{merge_as}.{name}"),
|
||||||
.map(|(name, idx)| (format!("{merge_as}.{name}"), idx + ref_offset)),
|
ElemReference::Direct(index + ref_offset),
|
||||||
);
|
),
|
||||||
|
ElemReference::Nested(index, sub_index) => (
|
||||||
|
format!("{merge_as}.{name}"),
|
||||||
|
ElemReference::Nested(index + ref_offset, sub_index),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
self.variables.extend(
|
self.variables.extend(
|
||||||
|
@ -80,44 +96,41 @@ pub trait Document<'a>: core::fmt::Debug {
|
||||||
|
|
||||||
/// Pushes a new element into the document's content
|
/// Pushes a new element into the document's content
|
||||||
fn push(&self, elem: Box<dyn Element>) {
|
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);
|
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>) {
|
fn add_variable(&self, variable: Rc<dyn Variable>) {
|
||||||
self.scope()
|
self.scope()
|
||||||
.borrow_mut()
|
.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`]
|
/// Merges [`other`] into [`self`]
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// If [`merge_as`] is None, references and variables from the other document are not merged into self
|
||||||
fn merge(
|
fn merge(
|
||||||
&self,
|
&self,
|
||||||
content: &RefCell<Vec<Box<dyn Element>>>,
|
content: &RefCell<Vec<Box<dyn Element>>>,
|
||||||
|
@ -182,6 +179,36 @@ pub trait Document<'a>: core::fmt::Debug {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.extend((content.borrow_mut()).drain(..).map(|value| value));
|
.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> {
|
pub trait DocumentAccessors<'a> {
|
||||||
|
|
|
@ -1,65 +1,108 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use crate::compiler::compiler::Compiler;
|
||||||
use crate::{compiler::compiler::Compiler, parser::source::Token};
|
use crate::parser::source::Token;
|
||||||
|
use downcast_rs::impl_downcast;
|
||||||
|
use downcast_rs::Downcast;
|
||||||
|
|
||||||
use super::document::Document;
|
use super::document::Document;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum ElemKind {
|
pub enum ElemKind {
|
||||||
/// An invisible element (i.e comment)
|
/// An invisible element (i.e comment)
|
||||||
Invisible,
|
Invisible,
|
||||||
/// Special elements don't trigger special formatting events
|
/// Special elements don't trigger special formatting events
|
||||||
Special,
|
Special,
|
||||||
/// Inline elements don't break paragraphing
|
/// Inline elements don't break paragraphing
|
||||||
Inline,
|
Inline,
|
||||||
/// Block elements are outside of paragraphs
|
/// Block elements are outside of paragraphs
|
||||||
Block,
|
Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for ElemKind {
|
impl FromStr for ElemKind {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s
|
match s {
|
||||||
{
|
|
||||||
"invisible" => Ok(ElemKind::Invisible),
|
"invisible" => Ok(ElemKind::Invisible),
|
||||||
"special" => Ok(ElemKind::Special),
|
"special" => Ok(ElemKind::Special),
|
||||||
"inline" => Ok(ElemKind::Inline),
|
"inline" => Ok(ElemKind::Inline),
|
||||||
"block" => Ok(ElemKind::Block),
|
"block" => Ok(ElemKind::Block),
|
||||||
_ => Err(format!("Unknown ElemKind: {s}"))
|
_ => Err(format!("Unknown ElemKind: {s}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Element: Downcast
|
pub trait Element: Downcast {
|
||||||
{
|
/// Gets the element defined location i.e token without filename
|
||||||
/// Gets the element defined location i.e token without filename
|
fn location(&self) -> &Token;
|
||||||
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;
|
fn element_name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Outputs element to string for debug purposes
|
/// Outputs element to string for debug purposes
|
||||||
fn to_string(&self) -> String;
|
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
|
/// Gets the element as a container containing other elements
|
||||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String>;
|
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_downcast!(Element);
|
||||||
|
|
||||||
impl core::fmt::Debug for dyn Element
|
impl core::fmt::Debug for dyn Element {
|
||||||
{
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
write!(f, "{}", self.to_string())
|
||||||
write!(f, "{}", self.to_string())
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ReferenceableElement : Element {
|
pub trait ReferenceableElement: Element {
|
||||||
/// Reference name
|
/// Reference name
|
||||||
fn reference_name(&self) -> Option<&String>;
|
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,10 @@ use crate::compiler::compiler::Compiler;
|
||||||
use crate::compiler::compiler::Target;
|
use crate::compiler::compiler::Target;
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::document::DocumentAccessors;
|
use crate::document::document::DocumentAccessors;
|
||||||
|
use crate::document::element::ContainerElement;
|
||||||
use crate::document::element::ElemKind;
|
use crate::document::element::ElemKind;
|
||||||
use crate::document::element::Element;
|
use crate::document::element::Element;
|
||||||
|
use crate::document::element::ReferenceableElement;
|
||||||
use crate::document::references::validate_refname;
|
use crate::document::references::validate_refname;
|
||||||
use crate::parser::parser::ReportColors;
|
use crate::parser::parser::ReportColors;
|
||||||
use crate::parser::rule::RegexRule;
|
use crate::parser::rule::RegexRule;
|
||||||
|
@ -54,36 +56,22 @@ impl FromStr for MediaType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MediaGroup {
|
struct Media {
|
||||||
pub(self) location: Token,
|
pub(self) location: Token,
|
||||||
pub(self) media: Vec<Media>,
|
pub(self) media: Vec<Box<dyn Element>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaGroup {
|
impl Element for Media {
|
||||||
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 {
|
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn location(&self) -> &Token { &self.location }
|
||||||
|
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Block }
|
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 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> {
|
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => {
|
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)]
|
#[derive(Debug)]
|
||||||
struct Media {
|
struct Medium {
|
||||||
pub(self) location: Token,
|
pub(self) location: Token,
|
||||||
pub(self) reference: String,
|
pub(self) reference: String,
|
||||||
pub(self) uri: String,
|
pub(self) uri: String,
|
||||||
|
@ -116,15 +126,17 @@ struct Media {
|
||||||
pub(self) description: Option<Paragraph>,
|
pub(self) description: Option<Paragraph>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Media {
|
impl Element for Medium {
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn location(&self) -> &Token { &self.location }
|
||||||
|
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Block }
|
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 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> {
|
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
|
@ -142,7 +154,19 @@ impl Element for Media {
|
||||||
MediaType::VIDEO => todo!(),
|
MediaType::VIDEO => todo!(),
|
||||||
MediaType::AUDIO => 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() {
|
if let Some(paragraph) = self.description.as_ref() {
|
||||||
match paragraph.compile(compiler, document) {
|
match paragraph.compile(compiler, document) {
|
||||||
Ok(res) => result.push_str(res.as_str()),
|
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 {
|
pub struct MediaRule {
|
||||||
re: [Regex; 1],
|
re: [Regex; 1],
|
||||||
properties: PropertyParser,
|
properties: PropertyParser,
|
||||||
|
@ -178,6 +208,10 @@ impl MediaRule {
|
||||||
"width".to_string(),
|
"width".to_string(),
|
||||||
Property::new(false, "Override for the media width".to_string(), None),
|
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 {
|
Self {
|
||||||
re: [RegexBuilder::new(
|
re: [RegexBuilder::new(
|
||||||
r"^!\[(.*)\]\(((?:\\.|[^\\\\])*?)\)(?:\[((?:\\.|[^\\\\])*?)\])?((?:\\(?:.|\n)|[^\\\\])*?$)?",
|
r"^!\[(.*)\]\(((?:\\.|[^\\\\])*?)\)(?:\[((?:\\.|[^\\\\])*?)\])?((?:\\(?:.|\n)|[^\\\\])*?$)?",
|
||||||
|
@ -366,6 +400,13 @@ impl RegexRule for MediaRule {
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|(_, s)| Some(s));
|
.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) {
|
let description = match matches.get(4) {
|
||||||
Some(content) => {
|
Some(content) => {
|
||||||
let source = Rc::new(VirtualSource::new(
|
let source = Rc::new(VirtualSource::new(
|
||||||
|
@ -399,31 +440,30 @@ impl RegexRule for MediaRule {
|
||||||
None => panic!("Unknown error"),
|
None => panic!("Unknown error"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: caption
|
let mut group = match document.last_element_mut::<Media>() {
|
||||||
let mut group = match document.last_element_mut::<MediaGroup>() {
|
|
||||||
Some(group) => group,
|
Some(group) => group,
|
||||||
None => {
|
None => {
|
||||||
parser.push(
|
parser.push(
|
||||||
document,
|
document,
|
||||||
Box::new(MediaGroup {
|
Box::new(Media {
|
||||||
location: token.clone(),
|
location: token.clone(),
|
||||||
media: vec![],
|
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(),
|
location: token.clone(),
|
||||||
reference: refname,
|
reference: refname,
|
||||||
uri,
|
uri,
|
||||||
media_type,
|
media_type,
|
||||||
width,
|
width,
|
||||||
caption: None,
|
caption,
|
||||||
description,
|
description,
|
||||||
}) {
|
})) {
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Error, token.source(), token.start())
|
Report::build(ReportKind::Error, token.source(), token.start())
|
||||||
.with_message("Invalid Media")
|
.with_message("Invalid Media")
|
||||||
|
|
|
@ -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 regex::Regex;
|
||||||
use crate::{compiler::compiler::Target, document::document::Document, lua::kernel::CTX, parser::{parser::Parser, rule::RegexRule, source::{Source, Token}}};
|
use std::ops::Range;
|
||||||
use ariadne::{Report, Fmt, Label, ReportKind};
|
use std::rc::Rc;
|
||||||
use crate::{compiler::compiler::Compiler, document::element::{ElemKind, Element, ReferenceableElement}};
|
use std::sync::Arc;
|
||||||
use std::{ops::Range, rc::Rc, sync::Arc};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Section {
|
pub struct Section {
|
||||||
pub(self) location: Token,
|
pub(self) location: Token,
|
||||||
pub(self) title: String, // Section title
|
pub(self) title: String, // Section title
|
||||||
pub(self) depth: usize, // Section depth
|
pub(self) depth: usize, // Section depth
|
||||||
pub(self) kind: u8, // Section kind, e.g numbered, unnumbred, ...
|
pub(self) kind: u8, // Section kind, e.g numbered, unnumbred, ...
|
||||||
pub(self) reference: Option<String>, // Section reference name
|
pub(self) reference: Option<String>, // Section reference name
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Section
|
impl Element for Section {
|
||||||
{
|
fn location(&self) -> &Token { &self.location }
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn kind(&self) -> ElemKind { ElemKind::Block }
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Block }
|
fn element_name(&self) -> &'static str { "Section" }
|
||||||
fn element_name(&self) -> &'static str { "Section" }
|
fn to_string(&self) -> String { format!("{self:#?}") }
|
||||||
fn to_string(&self) -> String { format!("{self:#?}") }
|
fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) }
|
||||||
fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) }
|
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
match compiler.target() {
|
||||||
match compiler.target()
|
Target::HTML => Ok(format!(
|
||||||
{
|
"<h{0}>{1}</h{0}>",
|
||||||
Target::HTML => {
|
self.depth,
|
||||||
Ok(format!("<h{0}>{1}</h{0}>",
|
compiler.sanitize(self.title.as_str())
|
||||||
self.depth,
|
)),
|
||||||
compiler.sanitize(self.title.as_str())))
|
Target::LATEX => Err("Unimplemented compiler".to_string()),
|
||||||
}
|
}
|
||||||
Target::LATEX => Err("Unimplemented compiler".to_string())
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReferenceableElement for Section
|
impl ReferenceableElement for Section {
|
||||||
{
|
fn reference_name(&self) -> Option<&String> { self.reference.as_ref() }
|
||||||
fn reference_name(&self) -> Option<&String> { self.reference.as_ref() }
|
|
||||||
|
fn refcount_key(&self) -> &'static str { "section" }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SectionRule {
|
pub struct SectionRule {
|
||||||
|
@ -45,15 +60,16 @@ pub struct SectionRule {
|
||||||
|
|
||||||
impl SectionRule {
|
impl SectionRule {
|
||||||
pub fn new() -> Self {
|
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 mod section_kind {
|
||||||
{
|
pub const NONE: u8 = 0x00;
|
||||||
pub const NONE : u8 = 0x00;
|
pub const NO_TOC: u8 = 0x01;
|
||||||
pub const NO_TOC : u8 = 0x01;
|
pub const NO_NUMBER: u8 = 0x02;
|
||||||
pub const NO_NUMBER : u8 = 0x02;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegexRule for SectionRule {
|
impl RegexRule for SectionRule {
|
||||||
|
@ -61,11 +77,16 @@ impl RegexRule for SectionRule {
|
||||||
|
|
||||||
fn regexes(&self) -> &[Regex] { &self.re }
|
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 mut result = vec![];
|
||||||
let section_depth = match matches.get(1)
|
let section_depth = match matches.get(1) {
|
||||||
{
|
|
||||||
Some(depth) => {
|
Some(depth) => {
|
||||||
if depth.len() > 6 {
|
if depth.len() > 6 {
|
||||||
result.push(
|
result.push(
|
||||||
|
@ -78,16 +99,17 @@ impl RegexRule for SectionRule {
|
||||||
6.fg(parser.colors().info)))
|
6.fg(parser.colors().info)))
|
||||||
.with_color(parser.colors().error))
|
.with_color(parser.colors().error))
|
||||||
.finish());
|
.finish());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
depth.len()
|
depth.len()
|
||||||
},
|
}
|
||||||
_ => panic!("Empty section depth"),
|
_ => panic!("Empty section depth"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// [Optional] Reference name
|
// [Optional] Reference name
|
||||||
let section_refname = matches.get(2).map_or_else(|| None,
|
let section_refname = matches.get(2).map_or_else(
|
||||||
|
|| None,
|
||||||
|refname| {
|
|refname| {
|
||||||
/* TODO: Wait for reference rework
|
/* TODO: Wait for reference rework
|
||||||
// Check for duplicate reference
|
// Check for duplicate reference
|
||||||
|
@ -105,29 +127,28 @@ impl RegexRule for SectionRule {
|
||||||
refname.as_str().fg(parser.colors().highlight),
|
refname.as_str().fg(parser.colors().highlight),
|
||||||
reference.element_name().fg(parser.colors().highlight)))
|
reference.element_name().fg(parser.colors().highlight)))
|
||||||
.with_color(parser.colors().warning))
|
.with_color(parser.colors().warning))
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((ref_doc.source(), reference.location().start()+1..reference.location().end() ))
|
Label::new((ref_doc.source(), reference.location().start()+1..reference.location().end() ))
|
||||||
.with_message(format!("`{}` previously defined here",
|
.with_message(format!("`{}` previously defined here",
|
||||||
refname.as_str().fg(parser.colors().highlight)))
|
refname.as_str().fg(parser.colors().highlight)))
|
||||||
.with_color(parser.colors().warning))
|
.with_color(parser.colors().warning))
|
||||||
.with_note(format!("Previous reference was overwritten"))
|
.with_note(format!("Previous reference was overwritten"))
|
||||||
.finish());
|
.finish());
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Some(refname.as_str().to_string())
|
Some(refname.as_str().to_string())
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Section kind
|
// Section kind
|
||||||
let section_kind = match matches.get(3)
|
let section_kind = match matches.get(3) {
|
||||||
{
|
Some(kind) => match kind.as_str() {
|
||||||
Some(kind) => {
|
"*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC,
|
||||||
match kind.as_str() {
|
"*" => section_kind::NO_NUMBER,
|
||||||
"*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC,
|
"+" => section_kind::NO_TOC,
|
||||||
"*" => section_kind::NO_NUMBER,
|
"" => section_kind::NONE,
|
||||||
"+" => section_kind::NO_TOC,
|
_ => {
|
||||||
"" => section_kind::NONE,
|
result.push(
|
||||||
_ => {
|
|
||||||
result.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), kind.start())
|
Report::build(ReportKind::Error, token.source(), kind.start())
|
||||||
.with_message("Invalid section numbering kind")
|
.with_message("Invalid section numbering kind")
|
||||||
.with_label(
|
.with_label(
|
||||||
|
@ -139,38 +160,40 @@ impl RegexRule for SectionRule {
|
||||||
.with_color(parser.colors().error))
|
.with_color(parser.colors().error))
|
||||||
.with_help(format!("Leave empty for a numbered listed section"))
|
.with_help(format!("Leave empty for a numbered listed section"))
|
||||||
.finish());
|
.finish());
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => section_kind::NONE,
|
_ => section_kind::NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Spacing + Section name
|
// Spacing + Section name
|
||||||
let section_name = match matches.get(5)
|
let section_name = match matches.get(5) {
|
||||||
{
|
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let split = name.as_str().chars()
|
let split = name
|
||||||
|
.as_str()
|
||||||
|
.chars()
|
||||||
.position(|c| !c.is_whitespace())
|
.position(|c| !c.is_whitespace())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let section_name = &name.as_str()[split..];
|
let section_name = &name.as_str()[split..];
|
||||||
if section_name.is_empty() // No name
|
if section_name.is_empty()
|
||||||
|
// No name
|
||||||
{
|
{
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Missing section name")
|
.with_message("Missing section name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), name.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message("Sections require a name before line end")
|
.with_message("Sections require a name before line end")
|
||||||
.with_color(parser.colors().error))
|
.with_color(parser.colors().error),
|
||||||
.finish());
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No spacing
|
// No spacing
|
||||||
if split == 0
|
if split == 0 {
|
||||||
{
|
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Warning, token.source(), name.start())
|
Report::build(ReportKind::Warning, token.source(), name.start())
|
||||||
.with_message("Missing section spacing")
|
.with_message("Missing section spacing")
|
||||||
|
@ -183,57 +206,70 @@ impl RegexRule for SectionRule {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
section_name.to_string()
|
section_name.to_string()
|
||||||
},
|
}
|
||||||
_ => panic!("Empty section name")
|
_ => panic!("Empty section name"),
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.push(document, Box::new(
|
parser.push(
|
||||||
Section {
|
document,
|
||||||
|
Box::new(Section {
|
||||||
location: token.clone(),
|
location: token.clone(),
|
||||||
title: section_name,
|
title: section_name,
|
||||||
depth: section_depth,
|
depth: section_depth,
|
||||||
kind: section_kind,
|
kind: section_kind,
|
||||||
reference: section_refname
|
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![];
|
let mut bindings = vec![];
|
||||||
|
|
||||||
bindings.push(("push".to_string(), lua.create_function(
|
bindings.push((
|
||||||
|_, (title, depth, kind, reference) : (String, usize, String, Option<String>)| {
|
"push".to_string(),
|
||||||
let kind = match kind.as_str() {
|
lua.create_function(
|
||||||
"*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC,
|
|_, (title, depth, kind, reference): (String, usize, String, Option<String>)| {
|
||||||
"*" => section_kind::NO_NUMBER,
|
let kind = match kind.as_str() {
|
||||||
"+" => section_kind::NO_TOC,
|
"*+" | "+*" => section_kind::NO_NUMBER | section_kind::NO_TOC,
|
||||||
"" => section_kind::NONE,
|
"*" => section_kind::NO_NUMBER,
|
||||||
_ => return Err(BadArgument {
|
"+" => section_kind::NO_TOC,
|
||||||
to: Some("push".to_string()),
|
"" => section_kind::NONE,
|
||||||
pos: 3,
|
_ => {
|
||||||
name: Some("kind".to_string()),
|
return Err(BadArgument {
|
||||||
cause: Arc::new(mlua::Error::external(
|
to: Some("push".to_string()),
|
||||||
format!("Unknown section kind specified")))})
|
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.with_borrow(|ctx| {
|
||||||
ctx.parser.push(ctx.document, Box::new(Section {
|
ctx.as_ref().map(|ctx| {
|
||||||
location: ctx.location.clone(),
|
ctx.parser.push(
|
||||||
title,
|
ctx.document,
|
||||||
depth,
|
Box::new(Section {
|
||||||
kind,
|
location: ctx.location.clone(),
|
||||||
reference
|
title,
|
||||||
}));
|
depth,
|
||||||
}));
|
kind,
|
||||||
|
reference,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}).unwrap()));
|
|
||||||
|
|
||||||
bindings
|
bindings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -85,16 +85,15 @@ fn main() {
|
||||||
println!("-- END AST DEBUGGING --");
|
println!("-- END AST DEBUGGING --");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
if debug_opts.contains(&"ref".to_string())
|
||||||
//if debug_opts.contains(&"ref".to_string())
|
{
|
||||||
//{
|
println!("-- BEGIN REFERENCES DEBUGGING --");
|
||||||
// println!("-- BEGIN REFERENCES DEBUGGING --");
|
let sc = doc.scope().borrow();
|
||||||
// let sc = doc.scope.borrow();
|
sc.referenceable.iter().for_each(|(name, reference)| {
|
||||||
// sc.referenceable.iter().for_each(|(name, pos)| {
|
println!(" - {name}: `{:#?}`", doc.get_from_reference(reference));
|
||||||
// println!(" - {name}: `{:#?}`", doc.content.borrow()[*pos]);
|
});
|
||||||
// });
|
println!("-- END REFERENCES DEBUGGING --");
|
||||||
// println!("-- END REFERENCES DEBUGGING --");
|
}
|
||||||
//}
|
|
||||||
if debug_opts.contains(&"var".to_string()) {
|
if debug_opts.contains(&"var".to_string()) {
|
||||||
println!("-- BEGIN VARIABLES DEBUGGING --");
|
println!("-- BEGIN VARIABLES DEBUGGING --");
|
||||||
let sc = doc.scope().borrow();
|
let sc = doc.scope().borrow();
|
||||||
|
|
|
@ -10,6 +10,7 @@ use ariadne::Report;
|
||||||
|
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::document::DocumentAccessors;
|
use crate::document::document::DocumentAccessors;
|
||||||
|
use crate::document::element::DocumentEnd;
|
||||||
use crate::document::element::ElemKind;
|
use crate::document::element::ElemKind;
|
||||||
use crate::document::element::Element;
|
use crate::document::element::Element;
|
||||||
use crate::document::langdocument::LangDocument;
|
use crate::document::langdocument::LangDocument;
|
||||||
|
@ -227,6 +228,10 @@ impl Parser for LangParser {
|
||||||
.on_scope_end(self, &doc, super::state::Scope::DOCUMENT),
|
.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);
|
return Box::new(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue