This commit is contained in:
ef3d0c3e 2024-07-24 13:20:29 +02:00
parent b8f4671657
commit 104e102177
9 changed files with 709 additions and 570 deletions

83
src/cache/cache.rs vendored
View file

@ -1,29 +1,13 @@
use std::{error::Error, path::PathBuf}; use rusqlite::types::FromSql;
use rusqlite::Connection;
use rusqlite::ToSql;
use rusqlite::{types::FromSql, Connection, Params, ToSql}; pub enum CachedError<E> {
struct Cache {
con: Connection
}
impl Cache {
fn new(file: PathBuf) -> Result<Self, String> {
match Connection::open(file)
{
Err(e) => return Err(format!("Could not connect to cache database: {}", e.to_string())),
Ok(con) => Ok(Self { con })
}
}
}
pub enum CachedError<E>
{
SqlErr(rusqlite::Error), SqlErr(rusqlite::Error),
GenErr(E) GenErr(E),
} }
pub trait Cached pub trait Cached {
{
type Key; type Key;
type Value; type Value;
@ -39,10 +23,8 @@ pub trait Cached
fn key(&self) -> <Self as Cached>::Key; fn key(&self) -> <Self as Cached>::Key;
fn init(con: &mut Connection) -> Result<(), rusqlite::Error> fn init(con: &mut Connection) -> Result<(), rusqlite::Error> {
{ con.execute(<Self as Cached>::sql_table(), ()).map(|_| ())
con.execute(<Self as Cached>::sql_table(), ())
.map(|_| ())
} }
/// Attempts to retrieve a cached element from the compilation database /// Attempts to retrieve a cached element from the compilation database
@ -54,53 +36,50 @@ pub trait Cached
/// or if not cached, an error from the generator [`f`] /// or if not cached, an error from the generator [`f`]
/// ///
/// Note that on error, [`f`] may still have been called /// Note that on error, [`f`] may still have been called
fn cached<E, F>(&self, con: &mut Connection, f: F) fn cached<E, F>(
-> Result<<Self as Cached>::Value, CachedError<E>> &self,
con: &mut Connection,
f: F,
) -> Result<<Self as Cached>::Value, CachedError<E>>
where where
<Self as Cached>::Key: ToSql, <Self as Cached>::Key: ToSql,
<Self as Cached>::Value: FromSql + ToSql, <Self as Cached>::Value: FromSql + ToSql,
F: FnOnce(&Self) -> Result<<Self as Cached>::Value, E>, F: FnOnce(&Self) -> Result<<Self as Cached>::Value, E>,
{ {
let key = self.key(); let key = self.key();
// Find in cache // Find in cache
let mut query = match con.prepare(<Self as Cached>::sql_get_query()) let mut query = match con.prepare(<Self as Cached>::sql_get_query()) {
{
Ok(query) => query, Ok(query) => query,
Err(e) => return Err(CachedError::SqlErr(e)) Err(e) => return Err(CachedError::SqlErr(e)),
}; };
let value = query.query_row([&key], |row| let value = query
{ .query_row([&key], |row| {
Ok(row.get_unwrap::<_, <Self as Cached>::Value>(0)) Ok(row.get_unwrap::<_, <Self as Cached>::Value>(0))
}).ok(); })
.ok();
if let Some(value) = value if let Some(value) = value {
{
// Found in cache // Found in cache
return Ok(value) return Ok(value);
} } else {
else
{
// Compute a value // Compute a value
let value = match f(&self) let value = match f(&self) {
{
Ok(val) => val, Ok(val) => val,
Err(e) => return Err(CachedError::GenErr(e)) Err(e) => return Err(CachedError::GenErr(e)),
}; };
// Try to insert // Try to insert
let mut query = match con.prepare(<Self as Cached>::sql_insert_query()) let mut query = match con.prepare(<Self as Cached>::sql_insert_query()) {
{
Ok(query) => query, Ok(query) => query,
Err(e) => return Err(CachedError::SqlErr(e)) Err(e) => return Err(CachedError::SqlErr(e)),
}; };
match query.execute((&key, &value)) match query.execute((&key, &value)) {
{
Ok(_) => Ok(value), Ok(_) => Ok(value),
Err(e) => Err(CachedError::SqlErr(e)) Err(e) => Err(CachedError::SqlErr(e)),
} }
} }
} }
} }

View file

@ -1,4 +1,6 @@
use std::cell::{Ref, RefCell, RefMut}; use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::hash_map::HashMap; use std::collections::hash_map::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -7,14 +9,13 @@ use crate::parser::source::Source;
use super::element::Element; use super::element::Element;
use super::variable::Variable; use super::variable::Variable;
// TODO: Referenceable rework // TODO: Referenceable rework
// Usize based referencing is not an acceptable method // Usize based referencing is not an acceptable method
// if we want to support deltas for the lsp // if we want to support deltas for the lsp
#[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, usize>,
pub variables: HashMap<String, Rc<dyn Variable>>, pub variables: HashMap<String, Rc<dyn Variable>>,
} }
@ -27,38 +28,43 @@ impl Scope {
} }
} }
pub fn merge(&mut self, other: &mut Scope, merge_as: &String, ref_offset: usize) pub fn merge(&mut self, other: &mut Scope, merge_as: &String, ref_offset: usize) {
{ match merge_as.is_empty() {
match merge_as.is_empty() true => {
{ // References
true => { self.referenceable.extend(
// References other
self.referenceable.extend(other.referenceable.drain() .referenceable
.map(|(name, idx)| .drain()
(name, idx+ref_offset))); .map(|(name, idx)| (name, idx + ref_offset)),
);
// Variables // Variables
self.variables.extend(other.variables.drain() self.variables
.map(|(name, var)| .extend(other.variables.drain().map(|(name, var)| (name, var)));
(name, var))); }
}, false => {
false => { // References
// References self.referenceable.extend(
self.referenceable.extend(other.referenceable.drain() other
.map(|(name, idx)| .referenceable
(format!("{merge_as}.{name}"), idx+ref_offset))); .drain()
.map(|(name, idx)| (format!("{merge_as}.{name}"), idx + ref_offset)),
);
// Variables // Variables
self.variables.extend(other.variables.drain() self.variables.extend(
.map(|(name, var)| other
(format!("{merge_as}.{name}"), var))); .variables
} .drain()
} .map(|(name, var)| (format!("{merge_as}.{name}"), var)),
} );
}
}
}
} }
pub trait Document<'a>: core::fmt::Debug pub trait Document<'a>: core::fmt::Debug {
{
/// Gets the document [`Source`] /// Gets the document [`Source`]
fn source(&self) -> Rc<dyn Source>; fn source(&self) -> Rc<dyn Source>;
@ -73,16 +79,12 @@ pub trait Document<'a>: core::fmt::Debug
fn scope(&self) -> &RefCell<Scope>; fn scope(&self) -> &RefCell<Scope>;
/// 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 // TODO: RefTable
self.content() self.content().borrow_mut().push(elem);
.borrow_mut()
.push(elem);
} }
/* /*
fn last_element(&'a self, recurse: bool) -> Option<Ref<'_, dyn Element>> fn last_element(&'a self, recurse: bool) -> Option<Ref<'_, dyn Element>>
{ {
@ -116,88 +118,89 @@ pub trait Document<'a>: core::fmt::Debug
} }
*/ */
fn add_variable(&self, variable: Rc<dyn Variable>) fn add_variable(&self, variable: Rc<dyn Variable>) {
{ self.scope()
self.scope().borrow_mut().variables.insert( .borrow_mut()
variable.name().to_string(), .variables
variable); .insert(variable.name().to_string(), variable);
}
fn get_variable(&self, name: &str) -> Option<Rc<dyn Variable>>
{
match self.scope().borrow().variables.get(name)
{
Some(variable) => {
return Some(variable.clone());
},
// Continue search recursively
None => match self.parent() {
Some(parent) => return parent.get_variable(name),
// Not found
None => return None,
}
}
}
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`] fn get_variable(&self, name: &str) -> Option<Rc<dyn Variable>> {
fn merge(&self, content: &RefCell<Vec<Box<dyn Element>>>, scope: &RefCell<Scope>, merge_as: Option<&String>) match self.scope().borrow().variables.get(name) {
{ Some(variable) => {
match merge_as return Some(variable.clone());
}
// Continue search recursively
None => match self.parent() {
Some(parent) => return parent.get_variable(name),
// Not found
None => return None,
},
}
}
/*
fn remove_variable(&self, name: &str) -> Option<Rc<dyn Variable>>
{
match self.scope().borrow_mut().variables.remove(name)
{ {
Some(merge_as) => self.scope().borrow_mut() Some(variable) => {
.merge( return Some(variable.clone());
&mut *scope.borrow_mut(), },
merge_as,
self.content().borrow().len()+1), // Continue search recursively
_ => {}, None => match self.parent() {
Some(parent) => return parent.remove_variable(name),
// Not found
None => return None,
}
}
}
*/
/// Merges [`other`] into [`self`]
fn merge(
&self,
content: &RefCell<Vec<Box<dyn Element>>>,
scope: &RefCell<Scope>,
merge_as: Option<&String>,
) {
match merge_as {
Some(merge_as) => self.scope().borrow_mut().merge(
&mut *scope.borrow_mut(),
merge_as,
self.content().borrow().len() + 1,
),
_ => {}
} }
// Content // Content
self.content().borrow_mut().extend((content.borrow_mut()) self.content()
.drain(..) .borrow_mut()
.map(|value| value)); .extend((content.borrow_mut()).drain(..).map(|value| value));
} }
} }
pub trait DocumentAccessors<'a> pub trait DocumentAccessors<'a> {
{
fn last_element<T: Element>(&self) -> Option<Ref<'_, T>>; fn last_element<T: Element>(&self) -> Option<Ref<'_, T>>;
fn last_element_mut<T: Element>(&self) -> Option<RefMut<'_, T>>; fn last_element_mut<T: Element>(&self) -> Option<RefMut<'_, T>>;
} }
impl<'a> DocumentAccessors<'a> for dyn Document<'a> + '_ impl<'a> DocumentAccessors<'a> for dyn Document<'a> + '_ {
{ fn last_element<T: Element>(&self) -> Option<Ref<'_, T>> {
fn last_element<T: Element>(&self) -> Option<Ref<'_, T>> Ref::filter_map(self.content().borrow(), |content| {
{ content.last().and_then(|last| last.downcast_ref::<T>())
Ref::filter_map(self.content().borrow(), })
|content| content.last() .ok()
.and_then(|last| last.downcast_ref::<T>())).ok()
} }
fn last_element_mut<T: Element>(&self) -> Option<RefMut<'_, T>> fn last_element_mut<T: Element>(&self) -> Option<RefMut<'_, T>> {
{ RefMut::filter_map(self.content().borrow_mut(), |content| {
RefMut::filter_map(self.content().borrow_mut(), content.last_mut().and_then(|last| last.downcast_mut::<T>())
|content| content.last_mut() })
.and_then(|last| last.downcast_mut::<T>())).ok() .ok()
} }
} }

View file

@ -1,11 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Read;
use std::io::Write;
use std::ops::Range; use std::ops::Range;
use std::process::Command;
use std::process::Stdio;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr;
use std::sync::Once; use std::sync::Once;
use crate::parser::util::Property; use crate::parser::util::Property;
@ -19,10 +14,7 @@ use crypto::digest::Digest;
use crypto::sha2::Sha512; use crypto::sha2::Sha512;
use graphviz_rust::cmd::Format; use graphviz_rust::cmd::Format;
use graphviz_rust::cmd::Layout; use graphviz_rust::cmd::Layout;
use graphviz_rust::exec;
use graphviz_rust::exec_dot; use graphviz_rust::exec_dot;
use graphviz_rust::parse;
use graphviz_rust::printer::PrinterContext;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
use regex::Captures; use regex::Captures;
@ -129,6 +121,7 @@ impl Element for Graphviz {
} }
} }
}); });
// TODO: Format svg in a div
if let Some(mut con) = compiler.cache() { if let Some(mut con) = compiler.cache() {
match self.cached(&mut con, |s| s.dot_to_svg()) { match self.cached(&mut con, |s| s.dot_to_svg()) {

View file

@ -1,48 +1,60 @@
use mlua::{Function, Lua}; use crate::compiler::compiler::Compiler;
use regex::{Captures, Regex}; use crate::compiler::compiler::Target;
use serde::{Deserialize, Serialize}; use crate::document::document::Document;
use crate::parser::{parser::Parser, rule::RegexRule, source::{Source, Token}, util}; use crate::document::element::ElemKind;
use ariadne::{Report, Fmt, Label, ReportKind}; use crate::document::element::Element;
use crate::{compiler::compiler::{Compiler, Target}, document::{document::Document, element::{ElemKind, Element}}}; use crate::parser::parser::Parser;
use std::{ops::Range, rc::Rc}; use crate::parser::rule::RegexRule;
use crate::parser::source::Source;
use crate::parser::source::Token;
use crate::parser::util;
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Regex;
use std::ops::Range;
use std::rc::Rc;
#[derive(Debug)] #[derive(Debug)]
pub struct Link { pub struct Link {
location: Token, location: Token,
name: String, // Link name name: String, // Link name
url: String, // Link url url: String, // Link url
} }
impl Link impl Link {
{ pub fn new(location: Token, name: String, url: String) -> Self {
pub fn new(location: Token, name: String, url: String) -> Self { Self {
Self { location: location, name, url } location: location,
} name,
url,
}
}
} }
impl Element for Link impl Element for Link {
{ fn location(&self) -> &Token { &self.location }
fn location(&self) -> &Token { &self.location } fn kind(&self) -> ElemKind { ElemKind::Inline }
fn kind(&self) -> ElemKind { ElemKind::Inline } fn element_name(&self) -> &'static str { "Link" }
fn element_name(&self) -> &'static str { "Link" } fn to_string(&self) -> String { format!("{self:#?}") }
fn to_string(&self) -> String { format!("{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!(
{ "<a href=\"{}\">{}</a>",
Target::HTML => { compiler.sanitize(self.url.as_str()),
Ok(format!("<a href=\"{}\">{}</a>", compiler.sanitize(self.name.as_str()),
compiler.sanitize(self.url.as_str()), )),
compiler.sanitize(self.name.as_str()), Target::LATEX => Ok(format!(
)) "\\href{{{}}}{{{}}}",
}, compiler.sanitize(self.url.as_str()),
Target::LATEX => { compiler.sanitize(self.name.as_str()),
Ok(format!("\\href{{{}}}{{{}}}", )),
compiler.sanitize(self.url.as_str()), }
compiler.sanitize(self.name.as_str()), }
))
},
}
}
} }
pub struct LinkRule { pub struct LinkRule {
@ -51,7 +63,9 @@ pub struct LinkRule {
impl LinkRule { impl LinkRule {
pub fn new() -> Self { pub fn new() -> Self {
Self { re: [Regex::new(r"\[((?:\\.|[^\\\\])*?)\]\(((?:\\.|[^\\\\])*?)\)").unwrap()] } Self {
re: [Regex::new(r"\[((?:\\.|[^\\\\])*?)\]\(((?:\\.|[^\\\\])*?)\)").unwrap()],
}
} }
} }
@ -60,93 +74,100 @@ impl RegexRule for LinkRule {
fn regexes(&self) -> &[Regex] { &self.re } fn regexes(&self) -> &[Regex] { &self.re }
fn on_regex_match<'a>(&self, _: usize, parser: &dyn Parser, document: &'a dyn Document, token: Token, matches: Captures) fn on_regex_match<'a>(
-> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { &self,
_: usize,
parser: &dyn Parser,
document: &'a dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
let mut result = vec![]; let mut result = vec![];
let link_name = match matches.get(1) let link_name = match matches.get(1) {
{ Some(name) => {
Some(name) => { if name.as_str().is_empty() {
if name.as_str().is_empty()
{
result.push( result.push(
Report::build(ReportKind::Error, token.source(), name.start()) Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Empty link name") .with_message("Empty link name")
.with_label( .with_label(
Label::new((token.source().clone(), name.range())) Label::new((token.source().clone(), name.range()))
.with_message("Link name is empty") .with_message("Link name is empty")
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
.finish(),
);
return result; return result;
} }
// TODO: process into separate document... // TODO: process into separate document...
let text_content = util::process_text(document, name.as_str()); let text_content = util::process_text(document, name.as_str());
if text_content.as_str().is_empty() if text_content.as_str().is_empty() {
{
result.push( result.push(
Report::build(ReportKind::Error, token.source(), name.start()) Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Empty link name") .with_message("Empty link name")
.with_label( .with_label(
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!("Link name is empty. Once processed, `{}` yields `{}`", .with_message(format!(
name.as_str().fg(parser.colors().highlight), "Link name is empty. Once processed, `{}` yields `{}`",
text_content.as_str().fg(parser.colors().highlight), name.as_str().fg(parser.colors().highlight),
)) text_content.as_str().fg(parser.colors().highlight),
.with_color(parser.colors().error)) ))
.finish()); .with_color(parser.colors().error),
)
.finish(),
);
return result; return result;
} }
text_content text_content
}, }
_ => panic!("Empty link name"), _ => panic!("Empty link name"),
}; };
let link_url = match matches.get(2) let link_url = match matches.get(2) {
{ Some(url) => {
Some(url) => { if url.as_str().is_empty() {
if url.as_str().is_empty()
{
result.push( result.push(
Report::build(ReportKind::Error, token.source(), url.start()) Report::build(ReportKind::Error, token.source(), url.start())
.with_message("Empty link url") .with_message("Empty link url")
.with_label( .with_label(
Label::new((token.source(), url.range())) Label::new((token.source(), url.range()))
.with_message("Link url is empty") .with_message("Link url is empty")
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
.finish(),
);
return result; return result;
} }
let text_content = util::process_text(document, url.as_str()); let text_content = util::process_text(document, url.as_str());
if text_content.as_str().is_empty() if text_content.as_str().is_empty() {
{
result.push( result.push(
Report::build(ReportKind::Error, token.source(), url.start()) Report::build(ReportKind::Error, token.source(), url.start())
.with_message("Empty link url") .with_message("Empty link url")
.with_label( .with_label(
Label::new((token.source(), url.range())) Label::new((token.source(), url.range()))
.with_message(format!("Link url is empty. Once processed, `{}` yields `{}`", .with_message(format!(
url.as_str().fg(parser.colors().highlight), "Link url is empty. Once processed, `{}` yields `{}`",
text_content.as_str().fg(parser.colors().highlight), url.as_str().fg(parser.colors().highlight),
)) text_content.as_str().fg(parser.colors().highlight),
.with_color(parser.colors().error)) ))
.finish()); .with_color(parser.colors().error),
)
.finish(),
);
return result; return result;
} }
text_content text_content
}, }
_ => panic!("Empty link url"), _ => panic!("Empty link url"),
}; };
parser.push(document, Box::new( parser.push(
Link::new( document,
token.clone(), Box::new(Link::new(token.clone(), link_name, link_url)),
link_name, );
link_url
)
));
return result; return result;
} }
// TODO // TODO

View file

@ -95,7 +95,7 @@ impl Element for List
match_stack(&mut result, &ent.numbering); match_stack(&mut result, &ent.numbering);
result.push_str("<li>"); result.push_str("<li>");
match ent.content.iter().enumerate() match ent.content.iter().enumerate()
.try_for_each(|(idx, elem)| { .try_for_each(|(_idx, elem)| {
match elem.compile(compiler, document) { match elem.compile(compiler, document) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(s) => { result.push_str(s.as_str()); Ok(()) } Ok(s) => { result.push_str(s.as_str()); Ok(()) }

View file

@ -1,8 +1,30 @@
use mlua::{Error::BadArgument, Function, Lua}; use crate::compiler::compiler::Compiler;
use regex::{Captures, Regex}; use crate::document::document::Document;
use crate::{compiler::compiler::Compiler, document::{document::Document, element::{ElemKind, Element}}, lua::kernel::CTX, parser::{parser::Parser, rule::RegexRule, source::{Source, Token}, util::{self, Property, PropertyMapError, PropertyParser}}}; use crate::document::element::ElemKind;
use ariadne::{Fmt, Label, Report, ReportKind}; use crate::document::element::Element;
use std::{collections::HashMap, ops::Range, rc::Rc, str::FromStr, sync::Arc}; 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 crate::parser::util::Property;
use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser;
use crate::parser::util::{self};
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Regex;
use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
struct Raw { struct Raw {
@ -12,22 +34,26 @@ struct Raw {
} }
impl Raw { impl Raw {
fn new(location: Token, kind: ElemKind, content: String) -> Self { fn new(location: Token, kind: ElemKind, content: String) -> Self {
Self { location, kind, content } Self {
} location,
kind,
content,
}
}
} }
impl Element for Raw { impl Element for Raw {
fn location(&self) -> &Token { &self.location } fn location(&self) -> &Token { &self.location }
fn kind(&self) -> ElemKind { self.kind.clone() } fn kind(&self) -> ElemKind { self.kind.clone() }
fn element_name(&self) -> &'static str { "Raw" } fn element_name(&self) -> &'static str { "Raw" }
fn to_string(&self) -> String { format!("{self:#?}") } fn to_string(&self) -> String { format!("{self:#?}") }
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> { fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
Ok(self.content.clone()) Ok(self.content.clone())
} }
} }
pub struct RawRule { pub struct RawRule {
@ -38,172 +64,212 @@ pub struct RawRule {
impl RawRule { impl RawRule {
pub fn new() -> Self { pub fn new() -> Self {
let mut props = HashMap::new(); let mut props = HashMap::new();
props.insert("kind".to_string(), props.insert(
"kind".to_string(),
Property::new( Property::new(
true, true,
"Element display kind".to_string(), "Element display kind".to_string(),
Some("inline".to_string()))); Some("inline".to_string()),
),
);
Self { Self {
re: [ re: [
Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?").unwrap() Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?")
.unwrap(),
], ],
properties: PropertyParser::new(props) properties: PropertyParser::new(props),
} }
} }
} }
impl RegexRule for RawRule impl RegexRule for RawRule {
{ fn name(&self) -> &'static str { "Raw" }
fn name(&self) -> &'static str { "Raw" }
fn regexes(&self) -> &[regex::Regex] { &self.re } fn regexes(&self) -> &[regex::Regex] { &self.re }
fn on_regex_match(&self, _index: usize, parser: &dyn Parser, document: &dyn Document, token: Token, matches: Captures) fn on_regex_match(
-> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { &self,
_index: usize,
parser: &dyn Parser,
document: &dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut reports = vec![];
let raw_content = match matches.get(2) let raw_content = match matches.get(2) {
{
// Unterminated // Unterminated
None => { None => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) Report::build(ReportKind::Error, token.source(), token.start())
.with_message("Unterminated Raw Code") .with_message("Unterminated Raw Code")
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!("Missing terminating `{}` after first `{}`", .with_message(format!(
"?}".fg(parser.colors().info), "Missing terminating `{}` after first `{}`",
"{?".fg(parser.colors().info))) "?}".fg(parser.colors().info),
.with_color(parser.colors().error)) "{?".fg(parser.colors().info)
.finish()); ))
return reports; .with_color(parser.colors().error),
)
.finish(),
);
return reports;
} }
Some(content) => { Some(content) => {
let processed = util::process_escaped('\\', "?}", let processed =
content.as_str().trim_start().trim_end()); util::process_escaped('\\', "?}", content.as_str().trim_start().trim_end());
if processed.is_empty() if processed.is_empty() {
{
reports.push( reports.push(
Report::build(ReportKind::Warning, token.source(), content.start()) Report::build(ReportKind::Warning, token.source(), content.start())
.with_message("Empty Raw Code") .with_message("Empty Raw Code")
.with_label( .with_label(
Label::new((token.source().clone(), content.range())) Label::new((token.source().clone(), content.range()))
.with_message("Raw code is empty") .with_message("Raw code is empty")
.with_color(parser.colors().warning)) .with_color(parser.colors().warning),
.finish()); )
.finish(),
);
} }
processed processed
} }
}; };
let properties = match matches.get(1) let properties = match matches.get(1) {
{
None => match self.properties.default() { None => match self.properties.default() {
Ok(properties) => properties, Ok(properties) => properties,
Err(e) => { Err(e) => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) Report::build(ReportKind::Error, token.source(), token.start())
.with_message("Invalid Raw Code") .with_message("Invalid Raw Code")
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!("Raw code is missing properties: {e}")) .with_message(format!("Raw code is missing properties: {e}"))
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
return reports; .finish(),
}, );
} return reports;
}
},
Some(props) => { Some(props) => {
let processed = util::process_escaped('\\', "]", let processed =
props.as_str().trim_start().trim_end()); util::process_escaped('\\', "]", props.as_str().trim_start().trim_end());
match self.properties.parse(processed.as_str()) match self.properties.parse(processed.as_str()) {
{
Err(e) => { Err(e) => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), props.start()) Report::build(ReportKind::Error, token.source(), props.start())
.with_message("Invalid Raw Code Properties") .with_message("Invalid Raw Code Properties")
.with_label( .with_label(
Label::new((token.source().clone(), props.range())) Label::new((token.source().clone(), props.range()))
.with_message(e) .with_message(e)
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
.finish(),
);
return reports; return reports;
} }
Ok(properties) => properties Ok(properties) => properties,
} }
} }
}; };
let raw_kind : ElemKind = match properties.get("kind", let raw_kind: ElemKind = match properties.get("kind", |prop, value| {
|prop, value| ElemKind::from_str(value.as_str()).map_err(|e| (prop, e))) ElemKind::from_str(value.as_str()).map_err(|e| (prop, e))
{ }) {
Ok((_prop, kind)) => kind, Ok((_prop, kind)) => kind,
Err(e) => match e { Err(e) => match e {
PropertyMapError::ParseError((prop, err)) => { PropertyMapError::ParseError((prop, err)) => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) Report::build(ReportKind::Error, token.source(), token.start())
.with_message("Invalid Raw Code Property") .with_message("Invalid Raw Code Property")
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!("Property `kind: {}` cannot be converted: {}", .with_message(format!(
prop.fg(parser.colors().info), "Property `kind: {}` cannot be converted: {}",
err.fg(parser.colors().error))) prop.fg(parser.colors().info),
.with_color(parser.colors().warning)) err.fg(parser.colors().error)
.finish()); ))
return reports; .with_color(parser.colors().warning),
}, )
.finish(),
);
return reports;
}
PropertyMapError::NotFoundError(err) => { PropertyMapError::NotFoundError(err) => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) Report::build(ReportKind::Error, token.source(), token.start())
.with_message("Invalid Code Property") .with_message("Invalid Code Property")
.with_label( .with_label(
Label::new((token.source().clone(), token.start()+1..token.end())) Label::new((
.with_message(format!("Property `{}` is missing", token.source().clone(),
err.fg(parser.colors().info))) token.start() + 1..token.end(),
.with_color(parser.colors().warning)) ))
.finish()); .with_message(format!(
return reports; "Property `{}` is missing",
err.fg(parser.colors().info)
))
.with_color(parser.colors().warning),
)
.finish(),
);
return reports;
} }
} },
}; };
parser.push(document, Box::new(Raw { parser.push(
location: token.clone(), document,
kind: raw_kind, Box::new(Raw {
content: raw_content location: token.clone(),
})); kind: raw_kind,
content: raw_content,
}),
);
reports reports
} }
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((
|_, (kind, content): (String, String)| { "push".to_string(),
// Validate kind lua.create_function(|_, (kind, content): (String, String)| {
let kind = match ElemKind::from_str(kind.as_str()) // Validate kind
{ let kind = match ElemKind::from_str(kind.as_str()) {
Ok(kind) => kind, Ok(kind) => kind,
Err(e) => return Err(BadArgument { Err(e) => {
to: Some("push".to_string()), return Err(BadArgument {
pos: 1, to: Some("push".to_string()),
name: Some("kind".to_string()), pos: 1,
cause: Arc::new(mlua::Error::external( name: Some("kind".to_string()),
format!("Wrong section kind specified: {e}")))}) cause: Arc::new(mlua::Error::external(format!(
}; "Wrong section kind specified: {e}"
))),
})
}
};
CTX.with_borrow(|ctx| ctx.as_ref().map(|ctx| { CTX.with_borrow(|ctx| {
ctx.parser.push(ctx.document, Box::new(Raw { ctx.as_ref().map(|ctx| {
location: ctx.location.clone(), ctx.parser.push(
kind, ctx.document,
content, Box::new(Raw {
})); location: ctx.location.clone(),
})); kind,
content,
}),
);
})
});
Ok(())
})
.unwrap(),
));
Ok(())
}).unwrap()));
bindings bindings
} }
} }

View file

@ -17,20 +17,20 @@ use super::variable::VariableRule;
use super::variable::VariableSubstitutionRule; use super::variable::VariableSubstitutionRule;
pub fn register<P: Parser>(parser: &mut P) { pub fn register<P: Parser>(parser: &mut P) {
parser.add_rule(Box::new(CommentRule::new()), None); parser.add_rule(Box::new(CommentRule::new()), None).unwrap();
parser.add_rule(Box::new(ParagraphRule::new()), None); parser.add_rule(Box::new(ParagraphRule::new()), None).unwrap();
parser.add_rule(Box::new(ImportRule::new()), None); parser.add_rule(Box::new(ImportRule::new()), None).unwrap();
parser.add_rule(Box::new(ScriptRule::new()), None); parser.add_rule(Box::new(ScriptRule::new()), None).unwrap();
parser.add_rule(Box::new(VariableRule::new()), None); parser.add_rule(Box::new(VariableRule::new()), None).unwrap();
parser.add_rule(Box::new(VariableSubstitutionRule::new()), None); parser.add_rule(Box::new(VariableSubstitutionRule::new()), None).unwrap();
parser.add_rule(Box::new(RawRule::new()), None); parser.add_rule(Box::new(RawRule::new()), None).unwrap();
parser.add_rule(Box::new(ListRule::new()), None); parser.add_rule(Box::new(ListRule::new()), None).unwrap();
parser.add_rule(Box::new(CodeRule::new()), None); parser.add_rule(Box::new(CodeRule::new()), None).unwrap();
parser.add_rule(Box::new(TexRule::new()), None); parser.add_rule(Box::new(TexRule::new()), None).unwrap();
parser.add_rule(Box::new(GraphRule::new()), None); parser.add_rule(Box::new(GraphRule::new()), None).unwrap();
parser.add_rule(Box::new(StyleRule::new()), None); parser.add_rule(Box::new(StyleRule::new()), None).unwrap();
parser.add_rule(Box::new(SectionRule::new()), None); parser.add_rule(Box::new(SectionRule::new()), None).unwrap();
parser.add_rule(Box::new(LinkRule::new()), None); parser.add_rule(Box::new(LinkRule::new()), None).unwrap();
parser.add_rule(Box::new(TextRule::default()), None); parser.add_rule(Box::new(TextRule::default()), None).unwrap();
} }

View file

@ -1,211 +1,262 @@
use mlua::{Function, Lua}; use crate::document::document::Document;
use regex::{Captures, Regex}; use crate::lua::kernel::Kernel;
use crate::{document::document::Document, lua::kernel::{Kernel, KernelContext, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::RegexRule, source::{Source, Token, VirtualSource}, util}}; use crate::lua::kernel::KernelContext;
use ariadne::{Fmt, Label, Report, ReportKind}; use crate::parser::parser::Parser;
use std::{ops::Range, rc::Rc}; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule;
use crate::parser::source::Source;
use crate::parser::source::Token;
use crate::parser::source::VirtualSource;
use crate::parser::util;
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Regex;
use std::ops::Range;
use std::rc::Rc;
use super::text::Text; use super::text::Text;
pub struct ScriptRule pub struct ScriptRule {
{
re: [Regex; 2], re: [Regex; 2],
eval_kinds: [(&'static str, &'static str); 3] eval_kinds: [(&'static str, &'static str); 3],
} }
impl ScriptRule { impl ScriptRule {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
re: [ re: [
Regex::new(r"(?:^|\n)@<(?:(.*)\n?)((?:\\.|[^\\\\])*?)(?:\n?)>@").unwrap(), Regex::new(r"(?:^|\n)@<(?:(.*)\n?)((?:\\.|[^\\\\])*?)(?:\n?)>@").unwrap(),
Regex::new(r"%<(?:\[(.*?)\])?([^\s[:alpha:]])?((?:\\.|[^\\\\])*?)(?:\n?)>%").unwrap() Regex::new(r"%<(?:\[(.*?)\])?([^\s[:alpha:]])?((?:\\.|[^\\\\])*?)(?:\n?)>%")
.unwrap(),
], ],
eval_kinds: [ eval_kinds: [
("", "Eval"), ("", "Eval"),
("\"", "Eval to text"), ("\"", "Eval to text"),
("!", "Eval and parse"), ("!", "Eval and parse"),
] ],
} }
} }
fn validate_kernel_name(colors: &ReportColors, name: &str) fn validate_kernel_name(colors: &ReportColors, name: &str) -> Result<String, String> {
-> Result<String, String> {
let trimmed = name.trim_end().trim_start(); let trimmed = name.trim_end().trim_start();
if trimmed.is_empty() { return Ok("main".to_string()) } if trimmed.is_empty() {
else if trimmed.find(|c: char| c.is_whitespace()).is_some() { return Ok("main".to_string());
return Err(format!("Kernel name `{}` contains whitespaces", } else if trimmed.find(|c: char| c.is_whitespace()).is_some() {
trimmed.fg(colors.highlight))) return Err(format!(
"Kernel name `{}` contains whitespaces",
trimmed.fg(colors.highlight)
));
} }
Ok(trimmed.to_string()) Ok(trimmed.to_string())
} }
fn validate_kind(&self, colors: &ReportColors, kind: &str) fn validate_kind(&self, colors: &ReportColors, kind: &str) -> Result<usize, String> {
-> Result<usize, String> { match self
match self.eval_kinds.iter().position(|(kind_symbol, _)| kind == *kind_symbol) .eval_kinds
.iter()
.position(|(kind_symbol, _)| kind == *kind_symbol)
{ {
Some(id) => Ok(id), Some(id) => Ok(id),
None => Err(format!("Unable to find eval kind `{}`. Available kinds:{}", None => Err(format!(
kind.fg(colors.highlight), "Unable to find eval kind `{}`. Available kinds:{}",
self.eval_kinds.iter().fold(String::new(), |out, (symbol, name)| { kind.fg(colors.highlight),
self.eval_kinds
.iter()
.fold(String::new(), |out, (symbol, name)| {
out + format!("\n - '{symbol}' => {name}").as_str() out + format!("\n - '{symbol}' => {name}").as_str()
}))) })
)),
} }
} }
} }
impl RegexRule for ScriptRule impl RegexRule for ScriptRule {
{ fn name(&self) -> &'static str { "Script" }
fn name(&self) -> &'static str { "Script" }
fn regexes(&self) -> &[regex::Regex] { &self.re } fn regexes(&self) -> &[regex::Regex] { &self.re }
fn on_regex_match<'a>(&self, index: usize, parser: &dyn Parser, document: &'a dyn Document<'a>, token: Token, matches: Captures) fn on_regex_match<'a>(
-> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { &self,
index: usize,
parser: &dyn Parser,
document: &'a dyn Document<'a>,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut reports = vec![];
let kernel_name = match matches.get(1) { let kernel_name = match matches.get(1) {
None => "main".to_string(), None => "main".to_string(),
Some(name) => { Some(name) => match ScriptRule::validate_kernel_name(parser.colors(), name.as_str()) {
match ScriptRule::validate_kernel_name(parser.colors(), name.as_str()) Ok(name) => name,
{ Err(e) => {
Ok(name) => name, reports.push(
Err(e) => { Report::build(ReportKind::Error, token.source(), name.start())
reports.push(
Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Invalid kernel name") .with_message("Invalid kernel name")
.with_label( .with_label(
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(e) .with_message(e)
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
return reports; .finish(),
} );
return reports;
} }
} },
}; };
let kernel = parser.get_kernel(kernel_name.as_str()).unwrap_or_else(|| { let kernel = parser
parser.insert_kernel(kernel_name.to_string(), Kernel::new(parser)) .get_kernel(kernel_name.as_str())
}); .unwrap_or_else(|| parser.insert_kernel(kernel_name.to_string(), Kernel::new(parser)));
let kernel_data = matches.get(if index == 0 {2} else {3}) let kernel_data = matches
.and_then(|code| { .get(if index == 0 { 2 } else { 3 })
.and_then(|code| {
let trimmed = code.as_str().trim_start().trim_end(); let trimmed = code.as_str().trim_start().trim_end();
(!trimmed.is_empty()).then_some((trimmed, code.range())) (!trimmed.is_empty()).then_some((trimmed, code.range()))
}).or_else(|| { })
.or_else(|| {
reports.push( reports.push(
Report::build(ReportKind::Warning, token.source(), token.start()) Report::build(ReportKind::Warning, token.source(), token.start())
.with_message("Invalid kernel code") .with_message("Invalid kernel code")
.with_label( .with_label(
Label::new((token.source(), token.start()+1..token.end())) Label::new((token.source(), token.start() + 1..token.end()))
.with_message("Kernel code is empty") .with_message("Kernel code is empty")
.with_color(parser.colors().warning)) .with_color(parser.colors().warning),
.finish()); )
.finish(),
);
None None
}); });
if kernel_data.is_none() { return reports; } if kernel_data.is_none() {
return reports;
}
let (kernel_content, kernel_range) = kernel_data.unwrap(); let (kernel_content, kernel_range) = kernel_data.unwrap();
let source = Rc::new(VirtualSource::new( let source = Rc::new(VirtualSource::new(
Token::new(kernel_range, token.source()), Token::new(kernel_range, token.source()),
format!("{}#{}:lua_kernel@{kernel_name}", token.source().name(), matches.get(0).unwrap().start()), format!(
util::process_escaped('\\', ">@", kernel_content) "{}#{}:lua_kernel@{kernel_name}",
token.source().name(),
matches.get(0).unwrap().start()
),
util::process_escaped('\\', ">@", kernel_content),
)) as Rc<dyn Source>; )) as Rc<dyn Source>;
let execute = |lua: &Lua| let execute = |lua: &Lua| {
{ let chunk = lua.load(source.content()).set_name(kernel_name);
let chunk = lua.load(source.content())
.set_name(kernel_name);
if index == 0 // Exec if index == 0
// Exec
{ {
if let Err(e) = chunk.exec() if let Err(e) = chunk.exec() {
{
reports.push( reports.push(
Report::build(ReportKind::Error, source.clone(), 0) Report::build(ReportKind::Error, source.clone(), 0)
.with_message("Invalid kernel code") .with_message("Invalid kernel code")
.with_label( .with_label(
Label::new((source.clone(), 0..source.content().len())) Label::new((source.clone(), 0..source.content().len()))
.with_message(format!("Kernel execution failed:\n{}", e.to_string())) .with_message(format!(
.with_color(parser.colors().error)) "Kernel execution failed:\n{}",
.finish()); e.to_string()
))
.with_color(parser.colors().error),
)
.finish(),
);
return reports; return reports;
} }
} } else
else // Eval // Eval
{ {
// Validate kind // Validate kind
let kind = match matches.get(2) { let kind = match matches.get(2) {
None => 0, None => 0,
Some(kind) => { Some(kind) => match self.validate_kind(parser.colors(), kind.as_str()) {
match self.validate_kind(parser.colors(), kind.as_str()) Ok(kind) => kind,
{ Err(msg) => {
Ok(kind) => kind, reports.push(
Err(msg) => { Report::build(ReportKind::Error, token.source(), kind.start())
reports.push(
Report::build(ReportKind::Error, token.source(), kind.start())
.with_message("Invalid kernel code kind") .with_message("Invalid kernel code kind")
.with_label( .with_label(
Label::new((token.source(), kind.range())) Label::new((token.source(), kind.range()))
.with_message(msg) .with_message(msg)
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
return reports; .finish(),
} );
return reports;
} }
} },
}; };
if kind == 0 // Eval if kind == 0
// Eval
{ {
if let Err(e) = chunk.eval::<()>() if let Err(e) = chunk.eval::<()>() {
{
reports.push( reports.push(
Report::build(ReportKind::Error, source.clone(), 0) Report::build(ReportKind::Error, source.clone(), 0)
.with_message("Invalid kernel code") .with_message("Invalid kernel code")
.with_label( .with_label(
Label::new((source.clone(), 0..source.content().len())) Label::new((source.clone(), 0..source.content().len()))
.with_message(format!("Kernel evaluation failed:\n{}", e.to_string())) .with_message(format!(
.with_color(parser.colors().error)) "Kernel evaluation failed:\n{}",
.finish()); e.to_string()
))
.with_color(parser.colors().error),
)
.finish(),
);
} }
} } else
else // Eval to string // Eval to string
{ {
match chunk.eval::<String>() match chunk.eval::<String>() {
{
Ok(result) => { Ok(result) => {
if kind == 1 // Eval to text if kind == 1
// Eval to text
{ {
if !result.is_empty() if !result.is_empty() {
{ parser.push(
parser.push(document, Box::new(Text::new( document,
Token::new(1..source.content().len(), source.clone()), Box::new(Text::new(
util::process_text(document, result.as_str()), Token::new(1..source.content().len(), source.clone()),
))); util::process_text(document, result.as_str()),
)),
);
} }
} } else if kind == 2
else if kind == 2 // Eval and Parse // Eval and Parse
{ {
let parse_source = Rc::new(VirtualSource::new( let parse_source = Rc::new(VirtualSource::new(
Token::new(0..source.content().len(), source.clone()), Token::new(0..source.content().len(), source.clone()),
format!("parse({})", source.name()), format!("parse({})", source.name()),
result result,
)) as Rc<dyn Source>; )) as Rc<dyn Source>;
parser.parse_into(parse_source, document); parser.parse_into(parse_source, document);
} }
}, }
Err(e) => { Err(e) => {
reports.push( reports.push(
Report::build(ReportKind::Error, source.clone(), 0) Report::build(ReportKind::Error, source.clone(), 0)
.with_message("Invalid kernel code") .with_message("Invalid kernel code")
.with_label( .with_label(
Label::new((source.clone(), 0..source.content().len())) Label::new((source.clone(), 0..source.content().len()))
.with_message(format!("Kernel evaluation failed:\n{}", e.to_string())) .with_message(format!(
.with_color(parser.colors().error)) "Kernel evaluation failed:\n{}",
.finish()); e.to_string()
))
.with_color(parser.colors().error),
)
.finish(),
);
} }
} }
} }
@ -217,11 +268,11 @@ impl RegexRule for ScriptRule
let ctx = KernelContext { let ctx = KernelContext {
location: Token::new(0..source.content().len(), source.clone()), location: Token::new(0..source.content().len(), source.clone()),
parser, parser,
document document,
}; };
kernel.run_with_context(ctx, execute) kernel.run_with_context(ctx, execute)
} }
// TODO // TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] } fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }

View file

@ -1,62 +1,88 @@
use mlua::{Function, Lua}; use std::any::Any;
use std::ops::Range;
use std::rc::Rc;
use crate::{compiler::compiler::Compiler, document::{document::Document, element::{ElemKind, Element}}, lua::kernel::CTX, parser::{parser::Parser, rule::Rule, source::Token}}; use ariadne::Report;
use mlua::Function;
use mlua::Lua;
use crate::compiler::compiler::Compiler;
use crate::document::document::Document;
use crate::document::element::ElemKind;
use crate::document::element::Element;
use crate::lua::kernel::CTX;
use crate::parser::parser::Parser;
use crate::parser::rule::Rule;
use crate::parser::source::Cursor;
use crate::parser::source::Source;
use crate::parser::source::Token;
#[derive(Debug)] #[derive(Debug)]
pub struct Text pub struct Text {
{ pub(self) location: Token,
pub(self) location: Token,
pub(self) content: String, pub(self) content: String,
} }
impl Text impl Text {
{ pub fn new(location: Token, content: String) -> Text {
pub fn new(location: Token, content: String) -> Text
{
Text { Text {
location: location, location: location,
content: content content: content,
} }
} }
} }
impl Element for Text impl Element for Text {
{ fn location(&self) -> &Token { &self.location }
fn location(&self) -> &Token { &self.location } fn kind(&self) -> ElemKind { ElemKind::Inline }
fn kind(&self) -> ElemKind { ElemKind::Inline }
fn element_name(&self) -> &'static str { "Text" } fn element_name(&self) -> &'static str { "Text" }
fn to_string(&self) -> String { format!("{self:#?}") } fn to_string(&self) -> String { format!("{self:#?}") }
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> { fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
Ok(compiler.sanitize(self.content.as_str())) Ok(compiler.sanitize(self.content.as_str()))
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct TextRule; pub struct TextRule;
impl Rule for TextRule impl Rule for TextRule {
{ fn name(&self) -> &'static str { "Text" }
fn name(&self) -> &'static str { "Text" }
fn next_match(&self, cursor: &crate::parser::source::Cursor) -> Option<(usize, Box<dyn std::any::Any>)> { None } fn next_match(&self, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None }
fn on_match(&self, parser: &dyn Parser, document: &dyn Document, cursor: crate::parser::source::Cursor, match_data: Option<Box<dyn std::any::Any>>) -> (crate::parser::source::Cursor, Vec<ariadne::Report<'_, (std::rc::Rc<dyn crate::parser::source::Source>, std::ops::Range<usize>)>>) { panic!("Text canno match"); } fn on_match(
&self,
_parser: &dyn Parser,
_document: &dyn Document,
_cursor: Cursor,
_match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
panic!("Text cannot match");
}
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((
|_, content: String| { "push".to_string(),
CTX.with_borrow(|ctx| ctx.as_ref().map(|ctx| { lua.create_function(|_, content: String| {
ctx.parser.push(ctx.document, Box::new(Text { CTX.with_borrow(|ctx| {
location: ctx.location.clone(), ctx.as_ref().map(|ctx| {
content, ctx.parser.push(
})); ctx.document,
})); Box::new(Text {
location: ctx.location.clone(),
content,
}),
);
})
});
Ok(())
})
.unwrap(),
));
Ok(())
}).unwrap()));
bindings bindings
} }
} }