Format
This commit is contained in:
parent
b8f4671657
commit
104e102177
9 changed files with 709 additions and 570 deletions
83
src/cache/cache.rs
vendored
83
src/cache/cache.rs
vendored
|
@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(()) }
|
||||||
|
|
|
@ -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(())
|
Ok(())
|
||||||
}).unwrap()));
|
})
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
bindings
|
bindings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
.get(if index == 0 { 2 } else { 3 })
|
||||||
.and_then(|code| {
|
.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![] }
|
||||||
|
|
|
@ -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(())
|
Ok(())
|
||||||
}).unwrap()));
|
})
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
bindings
|
bindings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue