Compare commits

..

No commits in common. "62e0aeeceffb49fea81635b44f688b8582ae9009" and "cf3491e5a7706979a8a9376fa4df7251a41cd279" have entirely different histories.

36 changed files with 1105 additions and 1215 deletions

View file

@ -0,0 +1,57 @@
use std::cell::Ref;
use std::cell::RefMut;
use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
use ariadne::Report;
use crate::parser::parser::Parser;
use crate::parser::source::Source;
use crate::parser::source::Token;
use super::document::Document;
#[derive(Debug, PartialEq, Eq)]
pub enum CustomStyleToken {
Toggle(String),
Pair(String, String),
}
pub trait CustomStyle: core::fmt::Debug {
/// Name for the custom style
fn name(&self) -> &str;
/// Gets the begin and end token for a custom style
fn tokens(&self) -> &CustomStyleToken;
fn on_start<'a>(
&self,
location: Token,
parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a),
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>>;
fn on_end<'a>(
&self,
location: Token,
parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a),
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>>;
}
pub trait CustomStyleHolder {
/// gets a reference to all defined custom styles
fn custom_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn CustomStyle>>>;
/// gets a (mutable) reference to all defined custom styles
fn custom_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn CustomStyle>>>;
fn get_custom_style(&self, style_name: &str) -> Option<Rc<dyn CustomStyle>> {
self.custom_styles()
.get(style_name)
.map(|style| style.clone())
}
fn insert_custom_style(&self, style: Rc<dyn CustomStyle>) {
self.custom_styles_mut().insert(style.name().into(), style);
}
}

View file

@ -1,24 +1,23 @@
use std::cell::RefCell; use std::{cell::RefCell, rc::Rc};
use std::rc::Rc;
use crate::parser::source::Source; use crate::parser::source::Source;
use super::document::Document; use super::{document::{Document, Scope}, element::Element};
use super::document::Scope;
use super::element::Element;
#[derive(Debug)] #[derive(Debug)]
pub struct LangDocument<'a> { pub struct LangDocument<'a> {
source: Rc<dyn Source>, source: Rc<dyn Source>,
parent: Option<&'a dyn Document<'a>>, parent: Option<&'a dyn Document<'a>>, /// Document's parent
/// Document's parent
// FIXME: Render these fields private // FIXME: Render these fields private
pub content: RefCell<Vec<Box<dyn Element>>>, pub content: RefCell<Vec<Box<dyn Element>>>,
pub scope: RefCell<Scope>, pub scope: RefCell<Scope>,
} }
impl<'a> LangDocument<'a> { impl<'a> LangDocument<'a>
pub fn new(source: Rc<dyn Source>, parent: Option<&'a dyn Document<'a>>) -> Self { {
pub fn new(source: Rc<dyn Source>, parent: Option<&'a dyn Document<'a>>) -> Self
{
Self { Self {
source: source, source: source,
parent: parent, parent: parent,
@ -29,13 +28,11 @@ impl<'a> LangDocument<'a> {
} }
impl<'a> Document<'a> for LangDocument<'a> { impl<'a> Document<'a> for LangDocument<'a> {
fn source(&self) -> Rc<dyn Source> { self.source.clone() } fn source(&self) -> Rc<dyn Source> { self.source.clone() }
fn parent(&self) -> Option<&'a dyn Document<'a>> { fn parent(&self) -> Option<&'a dyn Document<'a>> { self.parent.and_then(|p| Some(p as &dyn Document<'a>)) }
self.parent.and_then(|p| Some(p as &dyn Document<'a>))
}
fn content(&self) -> &RefCell<Vec<Box<dyn Element>>> { &self.content } fn content(&self) -> &RefCell<Vec<Box<dyn Element>>> { &self.content }
fn scope(&self) -> &RefCell<Scope> { &self.scope } fn scope(&self) -> &RefCell<Scope> { &self.scope }
} }

View file

@ -1,12 +1,15 @@
use std::any::Any; use std::any::Any;
use std::cell::Ref;
use std::cell::RefMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use crate::compiler::compiler::Compiler; use crate::compiler::compiler::Compiler;
use crate::document::document::Document;
use crate::elements::layout::LayoutToken; use crate::elements::layout::LayoutToken;
use super::document::Document;
/// Represents the type of a layout /// Represents the type of a layout
pub trait LayoutType: core::fmt::Debug { pub trait LayoutType: core::fmt::Debug {
/// Name of the layout /// Name of the layout
@ -29,17 +32,18 @@ pub trait LayoutType: core::fmt::Debug {
) -> Result<String, String>; ) -> Result<String, String>;
} }
#[derive(Default)] pub trait LayoutHolder {
pub struct LayoutHolder { /// gets a reference to all defined layouts
layouts: HashMap<String, Rc<dyn LayoutType>>, fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>>;
}
impl LayoutHolder { /// gets a (mutable) reference to all defined layours
pub fn get(&self, layout_name: &str) -> Option<Rc<dyn LayoutType>> { fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>>;
self.layouts.get(layout_name).map(|layout| layout.clone())
fn get_layout(&self, layout_name: &str) -> Option<Rc<dyn LayoutType>> {
self.layouts().get(layout_name).map(|layout| layout.clone())
} }
pub fn insert(&mut self, layout: Rc<dyn LayoutType>) { fn insert_layout(&self, layout: Rc<dyn LayoutType>) {
self.layouts.insert(layout.name().into(), layout); self.layouts_mut().insert(layout.name().into(), layout);
} }
} }

View file

@ -3,3 +3,6 @@ pub mod references;
pub mod langdocument; pub mod langdocument;
pub mod element; pub mod element;
pub mod variable; pub mod variable;
pub mod style;
pub mod layout;
pub mod customstyle;

View file

@ -45,7 +45,6 @@ pub mod tests {
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::ParserState;
#[test] #[test]
fn validate_refname_tests() { fn validate_refname_tests() {
@ -55,7 +54,7 @@ pub mod tests {
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
assert_eq!(validate_refname(&*doc, " abc ", true), Ok("abc")); assert_eq!(validate_refname(&*doc, " abc ", true), Ok("abc"));
assert_eq!( assert_eq!(

View file

@ -1,3 +1,5 @@
use std::cell::Ref;
use std::cell::RefMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -25,53 +27,48 @@ pub trait ElementStyle: Downcast + core::fmt::Debug {
} }
impl_downcast!(ElementStyle); impl_downcast!(ElementStyle);
#[derive(Default)] pub trait StyleHolder {
pub struct StyleHolder { /// gets a reference to all defined styles
styles: HashMap<String, Rc<dyn ElementStyle>>, fn element_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>>;
}
/// gets a (mutable) reference to all defined styles
fn element_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
impl StyleHolder {
/// Checks if a given style key is registered /// Checks if a given style key is registered
pub fn is_registered(&self, style_key: &str) -> bool { self.styles.contains_key(style_key) } fn is_style_registered(&self, style_key: &str) -> bool { self.element_styles().contains_key(style_key) }
/// Gets the current active style for an element /// Gets the current active style for an element
/// NOTE: Will panic if a style is not defined for a given element /// NOTE: Will panic if a style is not defined for a given element
/// If you need to process user input, use [`is_registered`] /// If you need to process user input, use [`is_registered`]
pub fn current(&self, style_key: &str) -> Rc<dyn ElementStyle> { fn current_style(&self, style_key: &str) -> Rc<dyn ElementStyle> {
self.styles.get(style_key).map(|rc| rc.clone()).unwrap() self.element_styles().get(style_key).map(|rc| rc.clone()).unwrap()
} }
/// Sets the [`style`] /// Sets the [`style`]
pub fn set_current(&mut self, style: Rc<dyn ElementStyle>) { fn set_current_style(&self, style: Rc<dyn ElementStyle>) {
self.styles.insert(style.key().to_string(), style); self.element_styles_mut().insert(style.key().to_string(), style);
} }
} }
#[macro_export] #[macro_export]
macro_rules! impl_elementstyle { macro_rules! impl_elementstyle {
($t:ty, $key:expr) => { ($t:ty, $key:expr) => {
impl crate::parser::style::ElementStyle for $t { impl ElementStyle for $t {
fn key(&self) -> &'static str { $key } fn key(&self) -> &'static str { $key }
fn from_json( fn from_json(&self, json: &str) -> Result<std::rc::Rc<dyn ElementStyle>, String> {
&self,
json: &str,
) -> Result<std::rc::Rc<dyn crate::parser::style::ElementStyle>, String> {
serde_json::from_str::<$t>(json) serde_json::from_str::<$t>(json)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
.map(|obj| { .map(|obj| std::rc::Rc::new(obj) as std::rc::Rc<dyn ElementStyle>)
std::rc::Rc::new(obj) as std::rc::Rc<dyn crate::parser::style::ElementStyle>
})
} }
fn from_lua( fn from_lua(
&self, &self,
lua: &mlua::Lua, lua: &mlua::Lua,
value: mlua::Value, value: mlua::Value,
) -> Result<std::rc::Rc<dyn crate::parser::style::ElementStyle>, mlua::Error> { ) -> Result<std::rc::Rc<dyn ElementStyle>, mlua::Error> {
mlua::LuaSerdeExt::from_value::<$t>(lua, value).map(|obj| { mlua::LuaSerdeExt::from_value::<$t>(lua, value)
std::rc::Rc::new(obj) as std::rc::Rc<dyn crate::parser::style::ElementStyle> .map(|obj| std::rc::Rc::new(obj) as std::rc::Rc<dyn ElementStyle>)
})
} }
} }
}; };

View file

@ -1,14 +1,11 @@
use std::{path::PathBuf, rc::Rc};
use crate::{elements::text::Text, parser::{parser::Parser, source::{Source, Token, VirtualSource}}};
use super::document::Document; use super::document::Document;
use crate::elements::text::Text;
use crate::parser::parser::ParserState;
use crate::parser::source::Source;
use crate::parser::source::Token;
use crate::parser::source::VirtualSource;
use std::path::PathBuf;
use std::rc::Rc;
// TODO enforce to_string(from_string(to_string())) == to_string() // TODO enforce to_string(from_string(to_string())) == to_string()
pub trait Variable { pub trait Variable
{
fn location(&self) -> &Token; fn location(&self) -> &Token;
fn name(&self) -> &str; fn name(&self) -> &str;
@ -18,99 +15,90 @@ pub trait Variable {
/// Converts variable to a string /// Converts variable to a string
fn to_string(&self) -> String; fn to_string(&self) -> String;
fn parse<'a>(&self, state: &ParserState, location: Token, document: &'a dyn Document<'a>); fn parse<'a>(&self, location: Token, parser: &dyn Parser, document: &'a dyn Document<'a>);
} }
impl core::fmt::Debug for dyn Variable { impl core::fmt::Debug for dyn Variable
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{{{}}}", self.name(), self.to_string()) write!(f, "{}{{{}}}", self.name(), self.to_string())
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct BaseVariable { pub struct BaseVariable
{
location: Token, location: Token,
name: String, name: String,
value: String, value: String,
} }
impl BaseVariable { impl BaseVariable {
pub fn new(location: Token, name: String, value: String) -> Self { pub fn new(location: Token, name: String, value: String) -> Self {
Self { Self { location, name, value }
location, }
name,
value,
}
}
} }
impl Variable for BaseVariable { impl Variable for BaseVariable
{
fn location(&self) -> &Token { &self.location } fn location(&self) -> &Token { &self.location }
fn name(&self) -> &str { self.name.as_str() } fn name(&self) -> &str { self.name.as_str() }
fn from_string(&mut self, str: &str) -> Option<String> { fn from_string(&mut self, str: &str) -> Option<String> {
self.value = str.to_string(); self.value = str.to_string();
None None
} }
fn to_string(&self) -> String { self.value.clone() } fn to_string(&self) -> String { self.value.clone() }
fn parse<'a>(&self, state: &ParserState, _location: Token, document: &'a dyn Document<'a>) { fn parse<'a>(&self, _location: Token, parser: &dyn Parser, document: &'a dyn Document<'a>) {
let source = Rc::new(VirtualSource::new( let source = Rc::new(VirtualSource::new(
self.location().clone(), self.location().clone(),
self.name().to_string(), self.name().to_string(),
self.to_string(), self.to_string()));
));
state.with_state(|new_state| { parser.parse_into(source, document);
let _ = new_state.parser.parse_into(new_state, source, document);
});
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct PathVariable { pub struct PathVariable
{
location: Token, location: Token,
name: String, name: String,
path: PathBuf, path: PathBuf,
} }
impl PathVariable { impl PathVariable
pub fn new(location: Token, name: String, path: PathBuf) -> Self { {
Self { pub fn new(location: Token, name: String, path: PathBuf) -> Self {
location, Self { location, name, path }
name, }
path,
}
}
} }
impl Variable for PathVariable { impl Variable for PathVariable
{
fn location(&self) -> &Token { &self.location } fn location(&self) -> &Token { &self.location }
fn name(&self) -> &str { self.name.as_str() } fn name(&self) -> &str { self.name.as_str() }
fn from_string(&mut self, str: &str) -> Option<String> { fn from_string(&mut self, str: &str) -> Option<String> {
self.path = PathBuf::from(std::fs::canonicalize(str).unwrap()); self.path = PathBuf::from(std::fs::canonicalize(str).unwrap());
None None
} }
fn to_string(&self) -> String { self.path.to_str().unwrap().to_string() } fn to_string(&self) -> String { self.path.to_str().unwrap().to_string() }
fn parse<'a>(&self, state: &ParserState, location: Token, document: &'a dyn Document) { fn parse<'a>(&self, location: Token, parser: &dyn Parser, document: &'a dyn Document) {
let source = Rc::new(VirtualSource::new( let source = Rc::new(VirtualSource::new(
location, location,
self.name().to_string(), self.name().to_string(),
self.to_string(), self.to_string()));
));
state.push( parser.push(document, Box::new(Text::new(
document, Token::new(0..source.content().len(), source),
Box::new(Text::new( self.to_string()
Token::new(0..source.content().len(), source), )));
self.to_string(), }
)),
);
}
} }

View file

@ -25,7 +25,7 @@ use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
@ -323,7 +323,7 @@ impl CodeRule {
) )
.unwrap(), .unwrap(),
], ],
properties: PropertyParser { properties: props }, properties: PropertyParser{ properties: props },
} }
} }
} }
@ -336,7 +336,7 @@ impl RegexRule for CodeRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
index: usize, index: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document, document: &'a dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -353,7 +353,7 @@ impl RegexRule for CodeRule {
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!("Code is missing properties: {e}")) .with_message(format!("Code is missing properties: {e}"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -371,7 +371,7 @@ impl RegexRule for CodeRule {
.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(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -393,7 +393,7 @@ impl RegexRule for CodeRule {
.with_label( .with_label(
Label::new((token.source().clone(), lang.range())) Label::new((token.source().clone(), lang.range()))
.with_message("No language specified") .with_message("No language specified")
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -411,9 +411,9 @@ impl RegexRule for CodeRule {
Label::new((token.source().clone(), lang.range())) Label::new((token.source().clone(), lang.range()))
.with_message(format!( .with_message(format!(
"Language `{}` cannot be found", "Language `{}` cannot be found",
code_lang.fg(state.parser.colors().info) code_lang.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -443,7 +443,7 @@ impl RegexRule for CodeRule {
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message("Code content cannot be empty") .with_message("Code content cannot be empty")
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -475,9 +475,9 @@ impl RegexRule for CodeRule {
.with_label( .with_label(
Label::new((token.source().clone(), token.start()+1..token.end())) Label::new((token.source().clone(), token.start()+1..token.end()))
.with_message(format!("Property `line_offset: {}` cannot be converted: {}", .with_message(format!("Property `line_offset: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(parser.colors().info),
err.fg(state.parser.colors().error))) err.fg(parser.colors().error)))
.with_color(state.parser.colors().warning)) .with_color(parser.colors().warning))
.finish()); .finish());
return reports; return reports;
} }
@ -492,9 +492,9 @@ impl RegexRule for CodeRule {
)) ))
.with_message(format!( .with_message(format!(
"Property `{}` doesn't exist", "Property `{}` doesn't exist",
err.fg(state.parser.colors().info) err.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -504,7 +504,7 @@ impl RegexRule for CodeRule {
} }
}; };
state.push( parser.push(
document, document,
Box::new(Code::new( Box::new(Code::new(
token.clone(), token.clone(),
@ -525,7 +525,7 @@ impl RegexRule for CodeRule {
CodeKind::Inline CodeKind::Inline
}; };
state.push( parser.push(
document, document,
Box::new(Code::new( Box::new(Code::new(
token.clone(), token.clone(),
@ -542,7 +542,7 @@ impl RegexRule for CodeRule {
reports reports
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
"push_inline".to_string(), "push_inline".to_string(),
@ -554,7 +554,7 @@ impl RegexRule for CodeRule {
.get_variable("code.theme") .get_variable("code.theme")
.and_then(|var| Some(var.to_string())); .and_then(|var| Some(var.to_string()));
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Code { Box::new(Code {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -585,7 +585,7 @@ impl RegexRule for CodeRule {
.get_variable("code.theme") .get_variable("code.theme")
.and_then(|var| Some(var.to_string())); .and_then(|var| Some(var.to_string()));
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Code { Box::new(Code {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -623,7 +623,7 @@ impl RegexRule for CodeRule {
.get_variable("code.theme") .get_variable("code.theme")
.and_then(|var| Some(var.to_string())); .and_then(|var| Some(var.to_string()));
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Code { Box::new(Code {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -644,7 +644,7 @@ impl RegexRule for CodeRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }
@ -652,7 +652,6 @@ impl RegexRule for CodeRule {
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
#[test] #[test]
@ -680,7 +679,7 @@ fn fact(n: usize) -> usize
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
let borrow = doc.content().borrow(); let borrow = doc.content().borrow();
let found = borrow let found = borrow
@ -726,7 +725,8 @@ fn fact(n: usize) -> usize
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); //let compiler = Compiler::new(Target::HTML, None);
let doc = parser.parse(source, None);
let borrow = doc.content().borrow(); let borrow = doc.content().borrow();
let found = borrow let found = borrow

View file

@ -2,13 +2,15 @@ use crate::compiler::compiler::Compiler;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::ops::Range; use std::ops::Range;
@ -58,7 +60,7 @@ impl RegexRule for CommentRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document, document: &'a dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -76,7 +78,7 @@ impl RegexRule for CommentRule {
.with_label( .with_label(
Label::new((token.source(), comment.range())) Label::new((token.source(), comment.range()))
.with_message("Comment is empty") .with_message("Comment is empty")
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -86,13 +88,7 @@ impl RegexRule for CommentRule {
} }
}; };
state.push( parser.push(document, Box::new(Comment::new(token.clone(), content)));
document,
Box::new(Comment {
location: token.clone(),
content,
}),
);
return reports; return reports;
} }
@ -101,10 +97,9 @@ impl RegexRule for CommentRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::style::Style; use crate::elements::style::Style;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -123,7 +118,7 @@ COMMENT ::Test
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {

View file

@ -1,6 +1,4 @@
use crate::lua::kernel::Kernel;
use std::any::Any; use std::any::Any;
use std::cell::Ref;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Range; use std::ops::Range;
@ -15,19 +13,21 @@ use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
use crate::document::customstyle::CustomStyle;
use crate::document::customstyle::CustomStyleToken;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::document::DocumentAccessors; use crate::document::document::DocumentAccessors;
use crate::lua::kernel::KernelContext; use crate::lua::kernel::KernelContext;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::customstyle::CustomStyle; use crate::parser::parser::Parser;
use crate::parser::customstyle::CustomStyleToken;
use crate::parser::parser::ParserState;
use crate::parser::rule::Rule; use crate::parser::rule::Rule;
use crate::parser::source::Cursor; use crate::parser::source::Cursor;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::state::RuleState;
use crate::parser::state::Scope; use crate::parser::state::Scope;
use crate::parser::state::State;
use lazy_static::lazy_static;
use super::paragraph::Paragraph; use super::paragraph::Paragraph;
@ -47,78 +47,75 @@ impl CustomStyle for LuaCustomStyle {
fn on_start<'a>( fn on_start<'a>(
&self, &self,
location: Token, location: Token,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>> {
let kernel: Ref<'_, Kernel> = let kernel = parser.get_kernel("main").unwrap();
Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap());
//let kernel = RefMut::map(parser_state.shared.kernels.borrow(), |ker| ker.get("main").unwrap());
let ctx = KernelContext { let ctx = KernelContext {
location: location.clone(), location: location.clone(),
state, parser,
document, document,
}; };
let mut reports = vec![]; let mut result = Ok(());
kernel.run_with_context(ctx, |lua| { kernel.run_with_context(ctx, |lua| {
let chunk = lua.load(self.start.as_str()); let chunk = lua.load(self.start.as_str());
if let Err(err) = chunk.eval::<()>() { if let Err(err) = chunk.eval::<()>() {
reports.push( result = Err(
Report::build(ReportKind::Error, location.source(), location.start()) Report::build(ReportKind::Error, location.source(), location.start())
.with_message("Lua execution failed") .with_message("Lua execution failed")
.with_label( .with_label(
Label::new((location.source(), location.range.clone())) Label::new((location.source(), location.range.clone()))
.with_message(err.to_string()) .with_message(err.to_string())
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_note(format!( .with_note(format!(
"When trying to start custom style {}", "When trying to start custom style {}",
self.name().fg(state.parser.colors().info) self.name().fg(parser.colors().info)
)) ))
.finish(), .finish(),
); );
} }
}); });
reports result
} }
fn on_end<'a>( fn on_end<'a>(
&self, &self,
location: Token, location: Token,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>> {
let kernel: Ref<'_, Kernel> = let kernel = parser.get_kernel("main").unwrap();
Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap());
let ctx = KernelContext { let ctx = KernelContext {
location: location.clone(), location: location.clone(),
state, parser,
document, document,
}; };
let mut reports = vec![]; let mut result = Ok(());
kernel.run_with_context(ctx, |lua| { kernel.run_with_context(ctx, |lua| {
let chunk = lua.load(self.end.as_str()); let chunk = lua.load(self.end.as_str());
if let Err(err) = chunk.eval::<()>() { if let Err(err) = chunk.eval::<()>() {
reports.push( result = Err(
Report::build(ReportKind::Error, location.source(), location.start()) Report::build(ReportKind::Error, location.source(), location.start())
.with_message("Lua execution failed") .with_message("Lua execution failed")
.with_label( .with_label(
Label::new((location.source(), location.range.clone())) Label::new((location.source(), location.range.clone()))
.with_message(err.to_string()) .with_message(err.to_string())
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_note(format!( .with_note(format!(
"When trying to end custom style {}", "When trying to end custom style {}",
self.name().fg(state.parser.colors().info) self.name().fg(parser.colors().info)
)) ))
.finish(), .finish(),
); );
} }
}); });
reports result
} }
} }
@ -126,12 +123,12 @@ struct CustomStyleState {
toggled: HashMap<String, Token>, toggled: HashMap<String, Token>,
} }
impl RuleState for CustomStyleState { impl State for CustomStyleState {
fn scope(&self) -> Scope { Scope::PARAGRAPH } fn scope(&self) -> Scope { Scope::PARAGRAPH }
fn on_remove<'a>( fn on_remove<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut reports = vec![];
@ -157,15 +154,15 @@ impl RuleState for CustomStyleState {
.with_order(1) .with_order(1)
.with_message(format!( .with_message(format!(
"Style {} starts here", "Style {} starts here",
style.fg(state.parser.colors().info) style.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_label( .with_label(
Label::new(paragraph_end) Label::new(paragraph_end)
.with_order(1) .with_order(1)
.with_message(format!("Paragraph ends here")) .with_message(format!("Paragraph ends here"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_note("Styles cannot span multiple documents (i.e @import)") .with_note("Styles cannot span multiple documents (i.e @import)")
.finish(), .finish(),
@ -176,22 +173,22 @@ impl RuleState for CustomStyleState {
} }
} }
static STATE_NAME: &'static str = "elements.custom_style";
pub struct CustomStyleRule; pub struct CustomStyleRule;
lazy_static! {
static ref STATE_NAME: String = "elements.custom_style".to_string();
}
impl Rule for CustomStyleRule { impl Rule for CustomStyleRule {
fn name(&self) -> &'static str { "Custom Style" } fn name(&self) -> &'static str { "Custom Style" }
fn next_match(&self, state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { fn next_match(&self, parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
let content = cursor.source.content(); let content = cursor.source.content();
let mut closest_match = usize::MAX; let mut closest_match = usize::MAX;
let mut matched_style = (None, false); let mut matched_style = (None, false);
state parser
.shared .custom_styles()
.custom_styles
.borrow()
.iter() .iter()
.for_each(|(_name, style)| match style.tokens() { .for_each(|(_name, style)| match style.tokens() {
CustomStyleToken::Toggle(s) => { CustomStyleToken::Toggle(s) => {
@ -231,61 +228,67 @@ impl Rule for CustomStyleRule {
fn on_match<'a>( fn on_match<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
cursor: Cursor, cursor: Cursor,
match_data: Box<dyn Any>, match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let (style, end) = match_data let (style, end) = match_data
.as_ref()
.unwrap()
.downcast_ref::<(Rc<dyn CustomStyle>, bool)>() .downcast_ref::<(Rc<dyn CustomStyle>, bool)>()
.unwrap(); .unwrap();
let mut rule_state_borrow = state.shared.rule_state.borrow_mut(); let query = parser.state().query(&STATE_NAME);
let style_state = match rule_state_borrow.get(STATE_NAME) { let state = match query {
Some(rule_state) => rule_state, Some(state) => state,
// Insert as a new state
None => { None => {
match rule_state_borrow.insert( // Insert as a new state
STATE_NAME.into(), match parser.state_mut().insert(
STATE_NAME.clone(),
Rc::new(RefCell::new(CustomStyleState { Rc::new(RefCell::new(CustomStyleState {
toggled: HashMap::new(), toggled: HashMap::new(),
})), })),
) { ) {
Err(err) => panic!("{err}"), Err(_) => panic!("Unknown error"),
Ok(rule_state) => rule_state, Ok(state) => state,
} }
} }
}; };
let (close, token) = match style.tokens() { let (close, token) = match style.tokens() {
CustomStyleToken::Toggle(s) => { CustomStyleToken::Toggle(s) => {
let mut borrow = style_state.as_ref().borrow_mut(); let mut borrow = state.borrow_mut();
let style_state = borrow.downcast_mut::<CustomStyleState>().unwrap(); let state = borrow.downcast_mut::<CustomStyleState>().unwrap();
if style_state.toggled.get(style.name()).is_some() { match state.toggled.get(style.name()) {
// Terminate style Some(_) => {
let token = Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone()); // Terminate style
let token =
Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
style_state.toggled.remove(style.name()); state.toggled.remove(style.name());
(true, token) (true, token)
} else { }
// Start style None => {
let token = Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone()); // Start style
let token =
Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
style_state state.toggled.insert(style.name().into(), token.clone());
.toggled (false, token)
.insert(style.name().into(), token.clone()); }
(false, token)
} }
} }
CustomStyleToken::Pair(s_begin, s_end) => { CustomStyleToken::Pair(s_begin, s_end) => {
let mut borrow = style_state.borrow_mut(); let mut borrow = state.borrow_mut();
let style_state = borrow.downcast_mut::<CustomStyleState>().unwrap(); let state = borrow.downcast_mut::<CustomStyleState>().unwrap();
if *end { if *end {
// Terminate style // Terminate style
let token = let token =
Token::new(cursor.pos..cursor.pos + s_end.len(), cursor.source.clone()); Token::new(cursor.pos..cursor.pos + s_end.len(), cursor.source.clone());
if style_state.toggled.get(style.name()).is_none() { if state.toggled.get(style.name()).is_none() {
return ( return (
cursor.at(cursor.pos + s_end.len()), cursor.at(cursor.pos + s_end.len()),
vec![ vec![
@ -296,16 +299,16 @@ impl Rule for CustomStyleRule {
.with_order(1) .with_order(1)
.with_message(format!( .with_message(format!(
"Cannot end style {} here, is it not started anywhere", "Cannot end style {} here, is it not started anywhere",
style.name().fg(state.parser.colors().info) style.name().fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
], ],
); );
} }
style_state.toggled.remove(style.name()); state.toggled.remove(style.name());
(true, token) (true, token)
} else { } else {
// Start style // Start style
@ -313,7 +316,7 @@ impl Rule for CustomStyleRule {
cursor.pos..cursor.pos + s_begin.len(), cursor.pos..cursor.pos + s_begin.len(),
cursor.source.clone(), cursor.source.clone(),
); );
if let Some(start_token) = style_state.toggled.get(style.name()) { if let Some(start_token) = state.toggled.get(style.name()) {
return ( return (
cursor.at(cursor.pos + s_end.len()), cursor.at(cursor.pos + s_end.len()),
vec![Report::build( vec![Report::build(
@ -327,43 +330,47 @@ impl Rule for CustomStyleRule {
.with_order(1) .with_order(1)
.with_message(format!( .with_message(format!(
"Style cannot {} starts here", "Style cannot {} starts here",
style.name().fg(state.parser.colors().info) style.name().fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_label( .with_label(
Label::new((start_token.source(), start_token.range.clone())) Label::new((start_token.source(), start_token.range.clone()))
.with_order(2) .with_order(2)
.with_message(format!( .with_message(format!(
"Style {} starts previously here", "Style {} starts previously here",
style.name().fg(state.parser.colors().info) style.name().fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish()], .finish()],
); );
} }
style_state state.toggled.insert(style.name().into(), token.clone());
.toggled
.insert(style.name().into(), token.clone());
(false, token) (false, token)
} }
} }
}; };
let reports = if close { if let Err(rep) = if close {
style.on_end(token.clone(), state, document) style.on_end(token.clone(), parser, document)
} else { } else {
style.on_start(token.clone(), state, document) style.on_start(token.clone(), parser, document)
}; } {
return (
(cursor.at(token.end()), unsafe { cursor.at(token.end()),
std::mem::transmute(reports) vec![unsafe {
}) // TODO
std::mem::transmute(rep)
}],
);
} else {
(cursor.at(token.end()), vec![])
}
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
@ -381,9 +388,7 @@ impl Rule for CustomStyleRule {
CTX.with_borrow(|ctx| { CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
if let Some(_) = if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
ctx.state.shared.custom_styles.borrow().get(name.as_str())
{
result = Err(BadArgument { result = Err(BadArgument {
to: Some("define_toggled".to_string()), to: Some("define_toggled".to_string()),
pos: 1, pos: 1,
@ -394,13 +399,7 @@ impl Rule for CustomStyleRule {
}); });
return; return;
} }
ctx.state ctx.parser.insert_custom_style(Rc::new(style));
.shared
.custom_styles
.borrow_mut()
.insert(Rc::new(style));
ctx.state.reset_match("Custom Style").unwrap();
}); });
}); });
@ -444,7 +443,7 @@ impl Rule for CustomStyleRule {
CTX.with_borrow(|ctx| { CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
if let Some(_) = ctx.state.shared.custom_styles.borrow().get(name.as_str()) { if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
result = Err(BadArgument { result = Err(BadArgument {
to: Some("define_paired".to_string()), to: Some("define_paired".to_string()),
pos: 1, pos: 1,
@ -455,9 +454,7 @@ impl Rule for CustomStyleRule {
}); });
return; return;
} }
ctx.state.shared.custom_styles.borrow_mut().insert(Rc::new(style)); ctx.parser.insert_custom_style(Rc::new(style));
ctx.state.reset_match("Custom Style").unwrap();
}); });
}); });
@ -467,7 +464,7 @@ impl Rule for CustomStyleRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }
@ -476,7 +473,6 @@ mod tests {
use crate::elements::raw::Raw; use crate::elements::raw::Raw;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -509,7 +505,7 @@ pre |styled| post °Hello°.
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {
@ -553,7 +549,7 @@ pre [styled] post (Hello).
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {

View file

@ -1,4 +1,3 @@
use crate::parser::style::ElementStyle;
use std::any::Any; use std::any::Any;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
@ -16,7 +15,7 @@ use regex::Regex;
use crate::document::document::Document; use crate::document::document::Document;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::Rule; use crate::parser::rule::Rule;
use crate::parser::source::Cursor; use crate::parser::source::Cursor;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -59,7 +58,7 @@ impl ElemStyleRule {
impl Rule for ElemStyleRule { impl Rule for ElemStyleRule {
fn name(&self) -> &'static str { "Element Style" } fn name(&self) -> &'static str { "Element Style" }
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re self.start_re
.find_at(cursor.source.content(), cursor.pos) .find_at(cursor.source.content(), cursor.pos)
.map_or(None, |m| { .map_or(None, |m| {
@ -69,10 +68,10 @@ impl Rule for ElemStyleRule {
fn on_match<'a>( fn on_match<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
_document: &'a (dyn Document<'a> + 'a), _document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let mut reports = vec![]; let mut reports = vec![];
let matches = self let matches = self
@ -81,7 +80,7 @@ impl Rule for ElemStyleRule {
.unwrap(); .unwrap();
let mut cursor = cursor.at(matches.get(0).unwrap().end() - 1); let mut cursor = cursor.at(matches.get(0).unwrap().end() - 1);
let style: Rc<dyn ElementStyle> = if let Some(key) = matches.get(1) { let style = if let Some(key) = matches.get(1) {
let trimmed = key.as_str().trim_start().trim_end(); let trimmed = key.as_str().trim_start().trim_end();
// Check if empty // Check if empty
@ -92,7 +91,7 @@ impl Rule for ElemStyleRule {
.with_label( .with_label(
Label::new((cursor.source.clone(), key.range())) Label::new((cursor.source.clone(), key.range()))
.with_message(format!("Expected a non-empty style key",)) .with_message(format!("Expected a non-empty style key",))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -100,7 +99,7 @@ impl Rule for ElemStyleRule {
} }
// Check if key exists // Check if key exists
if !state.shared.styles.borrow().is_registered(trimmed) { if !parser.is_style_registered(trimmed) {
reports.push( reports.push(
Report::build(ReportKind::Error, cursor.source.clone(), key.start()) Report::build(ReportKind::Error, cursor.source.clone(), key.start())
.with_message("Unknown Style Key") .with_message("Unknown Style Key")
@ -108,9 +107,9 @@ impl Rule for ElemStyleRule {
Label::new((cursor.source.clone(), key.range())) Label::new((cursor.source.clone(), key.range()))
.with_message(format!( .with_message(format!(
"Could not find a style with key: {}", "Could not find a style with key: {}",
trimmed.fg(state.parser.colors().info) trimmed.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -118,7 +117,7 @@ impl Rule for ElemStyleRule {
return (cursor, reports); return (cursor, reports);
} }
state.shared.styles.borrow().current(trimmed) parser.current_style(trimmed)
} else { } else {
panic!("Unknown error") panic!("Unknown error")
}; };
@ -136,7 +135,7 @@ impl Rule for ElemStyleRule {
.with_message(format!( .with_message(format!(
"Unable to parse json string after style key", "Unable to parse json string after style key",
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -158,10 +157,10 @@ impl Rule for ElemStyleRule {
)) ))
.with_message(format!( .with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}", "Failed to serialize `{}` into style with key `{}`: {err}",
json.fg(state.parser.colors().highlight), json.fg(parser.colors().highlight),
style.key().fg(state.parser.colors().info) style.key().fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -172,12 +171,12 @@ impl Rule for ElemStyleRule {
} }
}; };
state.shared.styles.borrow_mut().set_current(new_style); parser.set_current_style(new_style);
(cursor, reports) (cursor, reports)
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
@ -186,13 +185,7 @@ impl Rule for ElemStyleRule {
let mut result = Ok(()); let mut result = Ok(());
CTX.with_borrow(|ctx| { CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
if !ctx if !ctx.parser.is_style_registered(style_key.as_str()) {
.state
.shared
.styles
.borrow()
.is_registered(style_key.as_str())
{
result = Err(BadArgument { result = Err(BadArgument {
to: Some("set".to_string()), to: Some("set".to_string()),
pos: 1, pos: 1,
@ -204,7 +197,7 @@ impl Rule for ElemStyleRule {
return; return;
} }
let style = ctx.state.shared.styles.borrow().current(style_key.as_str()); let style = ctx.parser.current_style(style_key.as_str());
let new_style = match style.from_lua(lua, new_style) { let new_style = match style.from_lua(lua, new_style) {
Err(err) => { Err(err) => {
result = Err(err); result = Err(err);
@ -213,7 +206,7 @@ impl Rule for ElemStyleRule {
Ok(new_style) => new_style, Ok(new_style) => new_style,
}; };
ctx.state.shared.styles.borrow_mut().set_current(new_style); ctx.parser.set_current_style(new_style);
}) })
}); });
@ -222,6 +215,6 @@ impl Rule for ElemStyleRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }

View file

@ -3,7 +3,6 @@ use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Once; use std::sync::Once;
use crate::parser::parser::ParserState;
use crate::parser::util::Property; use crate::parser::util::Property;
use crate::parser::util::PropertyMapError; use crate::parser::util::PropertyMapError;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
@ -16,6 +15,8 @@ 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_dot; use graphviz_rust::exec_dot;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
@ -26,6 +27,7 @@ use crate::compiler::compiler::Target;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
@ -68,7 +70,7 @@ impl Graphviz {
let split_at = out.split_at(svg_start).1.find('\n').unwrap(); let split_at = out.split_at(svg_start).1.find('\n').unwrap();
let mut result = format!("<svg width=\"{}\"", self.width); let mut result = format!("<svg width=\"{}\"", self.width);
result.push_str(out.split_at(svg_start + split_at).1); result.push_str(out.split_at(svg_start+split_at).1);
result result
} }
@ -164,14 +166,18 @@ impl GraphRule {
); );
props.insert( props.insert(
"width".to_string(), "width".to_string(),
Property::new(true, "SVG width".to_string(), Some("100%".to_string())), Property::new(
true,
"SVG width".to_string(),
Some("100%".to_string()),
),
); );
Self { Self {
re: [Regex::new( re: [Regex::new(
r"\[graph\](?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\[/graph\])?", r"\[graph\](?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\[/graph\])?",
) )
.unwrap()], .unwrap()],
properties: PropertyParser { properties: props }, properties: PropertyParser{ properties: props },
} }
} }
} }
@ -184,7 +190,7 @@ impl RegexRule for GraphRule {
fn on_regex_match( fn on_regex_match(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -201,10 +207,10 @@ impl RegexRule for GraphRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Missing terminating `{}` after first `{}`", "Missing terminating `{}` after first `{}`",
"[/graph]".fg(state.parser.colors().info), "[/graph]".fg(parser.colors().info),
"[graph]".fg(state.parser.colors().info) "[graph]".fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -224,7 +230,7 @@ impl RegexRule for GraphRule {
.with_label( .with_label(
Label::new((token.source().clone(), content.range())) Label::new((token.source().clone(), content.range()))
.with_message("Graph code is empty") .with_message("Graph code is empty")
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -245,7 +251,7 @@ impl RegexRule for GraphRule {
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!("Graph is missing property: {e}")) .with_message(format!("Graph is missing property: {e}"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -263,7 +269,7 @@ impl RegexRule for GraphRule {
.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(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -288,10 +294,10 @@ impl RegexRule for GraphRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Property `layout: {}` cannot be converted: {}", "Property `layout: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(parser.colors().info),
err.fg(state.parser.colors().error) err.fg(parser.colors().error)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -307,7 +313,7 @@ impl RegexRule for GraphRule {
token.start() + 1..token.end(), token.start() + 1..token.end(),
)) ))
.with_message(err) .with_message(err)
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -334,19 +340,19 @@ impl RegexRule for GraphRule {
)) ))
.with_message(format!( .with_message(format!(
"Property `{}` is missing", "Property `{}` is missing",
err.fg(state.parser.colors().info) err.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
return reports; return reports;
} }
_ => panic!("Unknown error"), _ => panic!("Unknown error")
}, },
}; };
state.push( parser.push(
document, document,
Box::new(Graphviz { Box::new(Graphviz {
location: token, location: token,
@ -358,4 +364,7 @@ impl RegexRule for GraphRule {
reports reports
} }
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }

View file

@ -1,6 +1,6 @@
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::document::DocumentAccessors; use crate::document::document::DocumentAccessors;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -10,6 +10,8 @@ use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::ops::Range; use std::ops::Range;
@ -46,7 +48,7 @@ impl RegexRule for ImportRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -55,7 +57,7 @@ impl RegexRule for ImportRule {
// Path // Path
let import_file = match matches.get(2) { let import_file = match matches.get(2) {
Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) { Some(name) => match ImportRule::validate_name(parser.colors(), name.as_str()) {
Err(msg) => { Err(msg) => {
result.push( result.push(
Report::build(ReportKind::Error, token.source(), name.start()) Report::build(ReportKind::Error, token.source(), name.start())
@ -64,9 +66,9 @@ impl RegexRule for ImportRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Import name `{}` is invalid. {msg}", "Import name `{}` is invalid. {msg}",
name.as_str().fg(state.parser.colors().highlight) name.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -83,9 +85,9 @@ impl RegexRule for ImportRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Unable to access file `{}`", "Unable to access file `{}`",
filename.fg(state.parser.colors().highlight) filename.fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -102,9 +104,9 @@ impl RegexRule for ImportRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Path `{}` is not a file!", "Path `{}` is not a file!",
filename.fg(state.parser.colors().highlight) filename.fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -119,8 +121,7 @@ impl RegexRule for ImportRule {
// [Optional] import as // [Optional] import as
let import_as = match matches.get(1) { let import_as = match matches.get(1) {
Some(as_name) => match ImportRule::validate_as(state.parser.colors(), as_name.as_str()) Some(as_name) => match ImportRule::validate_as(parser.colors(), as_name.as_str()) {
{
Ok(as_name) => as_name, Ok(as_name) => as_name,
Err(msg) => { Err(msg) => {
result.push( result.push(
@ -130,9 +131,9 @@ impl RegexRule for ImportRule {
Label::new((token.source(), as_name.range())) Label::new((token.source(), as_name.range()))
.with_message(format!( .with_message(format!(
"Canot import `{import_file}` as `{}`. {msg}", "Canot import `{import_file}` as `{}`. {msg}",
as_name.as_str().fg(state.parser.colors().highlight) as_name.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -152,7 +153,7 @@ impl RegexRule for ImportRule {
.with_label( .with_label(
Label::new((token.source(), token.range)) Label::new((token.source(), token.range))
.with_message(format!("Failed to read content from path `{path}`")) .with_message(format!("Failed to read content from path `{path}`"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -160,15 +161,12 @@ impl RegexRule for ImportRule {
} }
}; };
state.with_state(|new_state| { let import_doc = parser.parse(import, Some(document));
let (import_doc, _) = new_state.parser.parse(new_state, import, Some(document)); document.merge(import_doc.content(), import_doc.scope(), Some(&import_as));
document.merge(import_doc.content(), import_doc.scope(), Some(&import_as));
});
// Close paragraph // Close paragraph
// TODO2: Check if this is safe to remove if document.last_element::<Paragraph>().is_some() {
if document.last_element::<Paragraph>().is_none() { parser.push(
state.push(
document, document,
Box::new(Paragraph { Box::new(Paragraph {
location: Token::new(token.end()..token.end(), token.source()), location: Token::new(token.end()..token.end(), token.source()),
@ -179,4 +177,6 @@ impl RegexRule for ImportRule {
return result; return result;
} }
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }

View file

@ -3,21 +3,21 @@ use crate::compiler::compiler::Target;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::layout::LayoutType;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::layout::LayoutHolder; use crate::parser::parser::Parser;
use crate::parser::layout::LayoutType;
use crate::parser::parser::ParserState;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::state::RuleState;
use crate::parser::state::Scope; use crate::parser::state::Scope;
use crate::parser::state::State;
use crate::parser::util::process_escaped; use crate::parser::util::process_escaped;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use lazy_static::lazy_static;
use mlua::Error::BadArgument; use mlua::Error::BadArgument;
use mlua::Function; use mlua::Function;
use mlua::Lua; use mlua::Lua;
@ -54,7 +54,6 @@ impl FromStr for LayoutToken {
} }
mod default_layouts { mod default_layouts {
use crate::parser::layout::LayoutType;
use crate::parser::util::Property; use crate::parser::util::Property;
use crate::parser::util::PropertyParser; use crate::parser::util::PropertyParser;
@ -243,12 +242,12 @@ struct LayoutState {
pub(self) stack: Vec<(Vec<Token>, Rc<dyn LayoutType>)>, pub(self) stack: Vec<(Vec<Token>, Rc<dyn LayoutType>)>,
} }
impl RuleState for LayoutState { impl State for LayoutState {
fn scope(&self) -> Scope { Scope::DOCUMENT } fn scope(&self) -> Scope { Scope::DOCUMENT }
fn on_remove<'a>( fn on_remove<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut reports = vec![];
@ -266,15 +265,15 @@ impl RuleState for LayoutState {
.with_order(1) .with_order(1)
.with_message(format!( .with_message(format!(
"Layout {} stars here", "Layout {} stars here",
layout_type.name().fg(state.parser.colors().info) layout_type.name().fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_label( .with_label(
Label::new((at.source(), at.range.clone())) Label::new((at.source(), at.range.clone()))
.with_order(2) .with_order(2)
.with_message("Document ends here".to_string()) .with_message("Document ends here".to_string())
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -314,17 +313,17 @@ impl LayoutRule {
} }
} }
pub fn initialize_state(state: &ParserState) -> Rc<RefCell<dyn RuleState>> { pub fn initialize_state(parser: &dyn Parser) -> Rc<RefCell<dyn State>> {
let mut rule_state_borrow = state.shared.rule_state.borrow_mut(); let query = parser.state().query(&STATE_NAME);
match rule_state_borrow.get(STATE_NAME) { match query {
Some(state) => state, Some(state) => state,
None => { None => {
// Insert as a new state // Insert as a new state
match rule_state_borrow.insert( match parser.state_mut().insert(
STATE_NAME.into(), STATE_NAME.clone(),
Rc::new(RefCell::new(LayoutState { stack: vec![] })), Rc::new(RefCell::new(LayoutState { stack: vec![] })),
) { ) {
Err(err) => panic!("{err}"), Err(_) => panic!("Unknown error"),
Ok(state) => state, Ok(state) => state,
} }
} }
@ -374,7 +373,9 @@ impl LayoutRule {
} }
} }
static STATE_NAME: &'static str = "elements.layout"; lazy_static! {
static ref STATE_NAME: String = "elements.layout".to_string();
}
impl RegexRule for LayoutRule { impl RegexRule for LayoutRule {
fn name(&self) -> &'static str { "Layout" } fn name(&self) -> &'static str { "Layout" }
@ -384,14 +385,14 @@ impl RegexRule for LayoutRule {
fn on_regex_match( fn on_regex_match(
&self, &self,
index: usize, index: usize,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut reports = vec![];
let rule_state = LayoutRule::initialize_state(state); let state = LayoutRule::initialize_state(parser);
if index == 0 if index == 0
// BEGIN_LAYOUT // BEGIN_LAYOUT
@ -405,9 +406,9 @@ impl RegexRule for LayoutRule {
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Missing layout name after `{}`", "Missing layout name after `{}`",
"#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) "#+BEGIN_LAYOUT".fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -425,9 +426,9 @@ impl RegexRule for LayoutRule {
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Empty layout name after `{}`", "Empty layout name after `{}`",
"#+BEGIN_LAYOUT".fg(state.parser.colors().highlight) "#+BEGIN_LAYOUT".fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -442,9 +443,9 @@ impl RegexRule for LayoutRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Missing a space before layout name `{}`", "Missing a space before layout name `{}`",
name.as_str().fg(state.parser.colors().highlight) name.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -452,7 +453,7 @@ impl RegexRule for LayoutRule {
} }
// Get layout // Get layout
let layout_type = match state.shared.layouts.borrow().get(trimmed) { let layout_type = match parser.get_layout(trimmed) {
None => { None => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), name.start()) Report::build(ReportKind::Error, token.source(), name.start())
@ -461,9 +462,9 @@ impl RegexRule for LayoutRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Cannot find layout `{}`", "Cannot find layout `{}`",
trimmed.fg(state.parser.colors().highlight) trimmed.fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -474,7 +475,7 @@ impl RegexRule for LayoutRule {
// Parse properties // Parse properties
let properties = match LayoutRule::parse_properties( let properties = match LayoutRule::parse_properties(
state.parser.colors(), parser.colors(),
&token, &token,
layout_type.clone(), layout_type.clone(),
matches.get(1), matches.get(1),
@ -486,7 +487,7 @@ impl RegexRule for LayoutRule {
} }
}; };
state.push( parser.push(
document, document,
Box::new(Layout { Box::new(Layout {
location: token.clone(), location: token.clone(),
@ -497,12 +498,11 @@ impl RegexRule for LayoutRule {
}), }),
); );
rule_state state
.as_ref()
.borrow_mut() .borrow_mut()
.downcast_mut::<LayoutState>() .downcast_mut::<LayoutState>()
.map_or_else( .map_or_else(
|| panic!("Invalid state at: `{STATE_NAME}`"), || panic!("Invalid state at: `{}`", STATE_NAME.as_str()),
|s| s.stack.push((vec![token.clone()], layout_type.clone())), |s| s.stack.push((vec![token.clone()], layout_type.clone())),
); );
} }
@ -513,10 +513,10 @@ impl RegexRule for LayoutRule {
let (id, token_type, layout_type, properties) = if index == 1 let (id, token_type, layout_type, properties) = if index == 1
// LAYOUT_NEXT // LAYOUT_NEXT
{ {
let mut rule_state_borrow = rule_state.as_ref().borrow_mut(); let mut state_borrow = state.borrow_mut();
let layout_state = rule_state_borrow.downcast_mut::<LayoutState>().unwrap(); let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
let (tokens, layout_type) = match layout_state.stack.last_mut() { let (tokens, layout_type) = match state.stack.last_mut() {
None => { None => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) Report::build(ReportKind::Error, token.source(), token.start())
@ -524,7 +524,7 @@ impl RegexRule for LayoutRule {
.with_label( .with_label(
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message("No active layout found".to_string()) .with_message("No active layout found".to_string())
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -543,10 +543,10 @@ impl RegexRule for LayoutRule {
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Layout expects a maximum of {} blocks, currently at {}", "Layout expects a maximum of {} blocks, currently at {}",
layout_type.expects().end.fg(state.parser.colors().info), layout_type.expects().end.fg(parser.colors().info),
tokens.len().fg(state.parser.colors().info), tokens.len().fg(parser.colors().info),
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -555,7 +555,7 @@ impl RegexRule for LayoutRule {
// Parse properties // Parse properties
let properties = match LayoutRule::parse_properties( let properties = match LayoutRule::parse_properties(
state.parser.colors(), parser.colors(),
&token, &token,
layout_type.clone(), layout_type.clone(),
matches.get(1), matches.get(1),
@ -576,10 +576,10 @@ impl RegexRule for LayoutRule {
) )
} else { } else {
// LAYOUT_END // LAYOUT_END
let mut rule_state_borrow = rule_state.as_ref().borrow_mut(); let mut state_borrow = state.borrow_mut();
let layout_state = rule_state_borrow.downcast_mut::<LayoutState>().unwrap(); let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
let (tokens, layout_type) = match layout_state.stack.last_mut() { let (tokens, layout_type) = match state.stack.last_mut() {
None => { None => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), token.start()) Report::build(ReportKind::Error, token.source(), token.start())
@ -587,7 +587,7 @@ impl RegexRule for LayoutRule {
.with_label( .with_label(
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message("No active layout found".to_string()) .with_message("No active layout found".to_string())
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -606,10 +606,10 @@ impl RegexRule for LayoutRule {
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Layout expects a minimum of {} blocks, currently at {}", "Layout expects a minimum of {} blocks, currently at {}",
layout_type.expects().start.fg(state.parser.colors().info), layout_type.expects().start.fg(parser.colors().info),
tokens.len().fg(state.parser.colors().info), tokens.len().fg(parser.colors().info),
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -618,7 +618,7 @@ impl RegexRule for LayoutRule {
// Parse properties // Parse properties
let properties = match LayoutRule::parse_properties( let properties = match LayoutRule::parse_properties(
state.parser.colors(), parser.colors(),
&token, &token,
layout_type.clone(), layout_type.clone(),
matches.get(1), matches.get(1),
@ -632,11 +632,11 @@ impl RegexRule for LayoutRule {
let layout_type = layout_type.clone(); let layout_type = layout_type.clone();
let id = tokens.len(); let id = tokens.len();
layout_state.stack.pop(); state.stack.pop();
(id, LayoutToken::End, layout_type, properties) (id, LayoutToken::End, layout_type, properties)
}; };
state.push( parser.push(
document, document,
Box::new(Layout { Box::new(Layout {
location: token, location: token,
@ -650,8 +650,8 @@ impl RegexRule for LayoutRule {
return reports; return reports;
} }
// TODO: Add method to create new layouts // TODO
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
@ -676,12 +676,11 @@ impl RegexRule for LayoutRule {
CTX.with_borrow(|ctx| { CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
// Make sure the rule state has been initialized // Make sure the state has been initialized
let rule_state = LayoutRule::initialize_state(ctx.state); let state = LayoutRule::initialize_state(ctx.parser);
// Get layout // Get layout
// let layout_type = match ctx.parser.get_layout(layout.as_str())
let layout_type = match ctx.state.shared.layouts.borrow().get(layout.as_str())
{ {
None => { None => {
result = Err(BadArgument { result = Err(BadArgument {
@ -713,7 +712,7 @@ impl RegexRule for LayoutRule {
let id = match layout_token { let id = match layout_token {
LayoutToken::Begin => { LayoutToken::Begin => {
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Layout { Box::new(Layout {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -724,21 +723,20 @@ impl RegexRule for LayoutRule {
}), }),
); );
rule_state state
.as_ref()
.borrow_mut() .borrow_mut()
.downcast_mut::<LayoutState>() .downcast_mut::<LayoutState>()
.map_or_else( .map_or_else(
|| panic!("Invalid state at: `{STATE_NAME}`"), || panic!("Invalid state at: `{}`", STATE_NAME.as_str()),
|s| s.stack.push((vec![ctx.location.clone()], layout_type.clone())), |s| s.stack.push((vec![ctx.location.clone()], layout_type.clone())),
); );
return; return;
}, },
LayoutToken::Next => { LayoutToken::Next => {
let mut state_borrow = rule_state.as_ref().borrow_mut(); let mut state_borrow = state.borrow_mut();
let layout_state = state_borrow.downcast_mut::<LayoutState>().unwrap(); let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
let (tokens, current_layout_type) = match layout_state.stack.last_mut() { let (tokens, current_layout_type) = match state.stack.last_mut() {
None => { None => {
result = Err(BadArgument { result = Err(BadArgument {
to: Some("push".to_string()), to: Some("push".to_string()),
@ -783,10 +781,10 @@ impl RegexRule for LayoutRule {
tokens.len() - 1 tokens.len() - 1
}, },
LayoutToken::End => { LayoutToken::End => {
let mut state_borrow = rule_state.as_ref().borrow_mut(); let mut state_borrow = state.borrow_mut();
let layout_state = state_borrow.downcast_mut::<LayoutState>().unwrap(); let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
let (tokens, current_layout_type) = match layout_state.stack.last_mut() { let (tokens, current_layout_type) = match state.stack.last_mut() {
None => { None => {
result = Err(BadArgument { result = Err(BadArgument {
to: Some("push".to_string()), to: Some("push".to_string()),
@ -828,12 +826,12 @@ impl RegexRule for LayoutRule {
} }
let id = tokens.len(); let id = tokens.len();
layout_state.stack.pop(); state.stack.pop();
id id
} }
}; };
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Layout { Box::new(Layout {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -852,12 +850,12 @@ impl RegexRule for LayoutRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
fn register_layouts(&self, holder: &mut LayoutHolder) { fn register_layouts(&self, parser: &dyn Parser) {
holder.insert(Rc::new(default_layouts::Centered::default())); parser.insert_layout(Rc::new(default_layouts::Centered::default()));
holder.insert(Rc::new(default_layouts::Split::default())); parser.insert_layout(Rc::new(default_layouts::Split::default()));
} }
} }
@ -866,7 +864,6 @@ mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -895,7 +892,7 @@ mod tests {
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Layout { token == LayoutToken::Begin, id == 0 }; Layout { token == LayoutToken::Begin, id == 0 };
@ -947,7 +944,7 @@ mod tests {
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Layout { token == LayoutToken::Begin, id == 0 }; Layout { token == LayoutToken::Begin, id == 0 };

View file

@ -5,7 +5,7 @@ use crate::document::element::ContainerElement;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
@ -91,7 +91,7 @@ impl RegexRule for LinkRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -107,7 +107,7 @@ impl RegexRule for LinkRule {
.with_label( .with_label(
Label::new((token.source().clone(), display.range())) Label::new((token.source().clone(), display.range()))
.with_message("Link name is empty") .with_message("Link name is empty")
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -122,10 +122,10 @@ impl RegexRule for LinkRule {
Label::new((token.source(), display.range())) Label::new((token.source(), display.range()))
.with_message(format!( .with_message(format!(
"Link name is empty. Once processed, `{}` yields `{}`", "Link name is empty. Once processed, `{}` yields `{}`",
display.as_str().fg(state.parser.colors().highlight), display.as_str().fg(parser.colors().highlight),
processed.fg(state.parser.colors().highlight), processed.fg(parser.colors().highlight),
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -137,7 +137,7 @@ impl RegexRule for LinkRule {
"Link Display".to_string(), "Link Display".to_string(),
processed, processed,
)); ));
match util::parse_paragraph(state, source, document) { match util::parse_paragraph(parser, source, document) {
Err(err) => { Err(err) => {
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), display.start()) Report::build(ReportKind::Error, token.source(), display.start())
@ -145,7 +145,7 @@ impl RegexRule for LinkRule {
.with_label( .with_label(
Label::new((token.source(), display.range())) Label::new((token.source(), display.range()))
.with_message(err.to_string()) .with_message(err.to_string())
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -166,7 +166,7 @@ impl RegexRule for LinkRule {
.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(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -182,10 +182,10 @@ impl RegexRule for LinkRule {
Label::new((token.source(), url.range())) Label::new((token.source(), url.range()))
.with_message(format!( .with_message(format!(
"Link url is empty. Once processed, `{}` yields `{}`", "Link url is empty. Once processed, `{}` yields `{}`",
url.as_str().fg(state.parser.colors().highlight), url.as_str().fg(parser.colors().highlight),
text_content.as_str().fg(state.parser.colors().highlight), text_content.as_str().fg(parser.colors().highlight),
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -196,7 +196,7 @@ impl RegexRule for LinkRule {
_ => panic!("Empty link url"), _ => panic!("Empty link url"),
}; };
state.push( parser.push(
document, document,
Box::new(Link { Box::new(Link {
location: token, location: token,
@ -208,7 +208,7 @@ impl RegexRule for LinkRule {
return reports; return reports;
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
@ -223,7 +223,7 @@ impl RegexRule for LinkRule {
display, display,
)); ));
let display_content = let display_content =
match util::parse_paragraph(ctx.state, source, ctx.document) { match util::parse_paragraph(ctx.parser, source, ctx.document) {
Err(err) => { Err(err) => {
result = Err(BadArgument { result = Err(BadArgument {
to: Some("push".to_string()), to: Some("push".to_string()),
@ -240,7 +240,7 @@ impl RegexRule for LinkRule {
} }
}; };
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Link { Box::new(Link {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -256,7 +256,7 @@ impl RegexRule for LinkRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }
@ -266,7 +266,6 @@ mod tests {
use crate::elements::style::Style; use crate::elements::style::Style;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -284,7 +283,7 @@ Some [link](url).
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {
@ -314,7 +313,7 @@ nml.link.push("**BOLD link**", "another url")
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {

View file

@ -11,7 +11,7 @@ use crate::document::document::DocumentAccessors;
use crate::document::element::ContainerElement; use crate::document::element::ContainerElement;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::Rule; use crate::parser::rule::Rule;
use crate::parser::source::Cursor; use crate::parser::source::Cursor;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -25,6 +25,8 @@ use crate::parser::util::PropertyParser;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Match; use regex::Match;
use regex::Regex; use regex::Regex;
@ -134,7 +136,7 @@ impl ListRule {
fn push_markers( fn push_markers(
token: &Token, token: &Token,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
current: &Vec<(bool, usize)>, current: &Vec<(bool, usize)>,
target: &Vec<(bool, usize)>, target: &Vec<(bool, usize)>,
@ -150,7 +152,7 @@ impl ListRule {
// Close // Close
for i in start_pos..current.len() { for i in start_pos..current.len() {
state.push( parser.push(
document, document,
Box::new(ListMarker { Box::new(ListMarker {
location: token.clone(), location: token.clone(),
@ -162,7 +164,7 @@ impl ListRule {
// Open // Open
for i in start_pos..target.len() { for i in start_pos..target.len() {
state.push( parser.push(
document, document,
Box::new(ListMarker { Box::new(ListMarker {
location: token.clone(), location: token.clone(),
@ -250,7 +252,7 @@ impl ListRule {
impl Rule for ListRule { impl Rule for ListRule {
fn name(&self) -> &'static str { "List" } fn name(&self) -> &'static str { "List" }
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re self.start_re
.find_at(cursor.source.content(), cursor.pos) .find_at(cursor.source.content(), cursor.pos)
.map_or(None, |m| { .map_or(None, |m| {
@ -260,10 +262,10 @@ impl Rule for ListRule {
fn on_match<'a>( fn on_match<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
cursor: Cursor, cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let mut reports = vec![]; let mut reports = vec![];
@ -293,7 +295,7 @@ impl Rule for ListRule {
.with_label( .with_label(
Label::new((cursor.source.clone(), properties.range())) Label::new((cursor.source.clone(), properties.range()))
.with_message(err) .with_message(err)
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -352,12 +354,12 @@ impl Rule for ListRule {
captures.get(1).unwrap().range(), captures.get(1).unwrap().range(),
)) ))
.with_message("Spacing for list entries do not match") .with_message("Spacing for list entries do not match")
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.with_label( .with_label(
Label::new((cursor.source.clone(), spacing.0.clone())) Label::new((cursor.source.clone(), spacing.0.clone()))
.with_message("Previous spacing") .with_message("Previous spacing")
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -377,7 +379,7 @@ impl Rule for ListRule {
"List Entry".to_string(), "List Entry".to_string(),
entry_content, entry_content,
)); ));
let parsed_content = match util::parse_paragraph(state, entry_src, document) { let parsed_content = match util::parse_paragraph(parser, entry_src, document) {
Err(err) => { Err(err) => {
reports.push( reports.push(
Report::build(ReportKind::Warning, token.source(), token.range.start) Report::build(ReportKind::Warning, token.source(), token.range.start)
@ -385,7 +387,7 @@ impl Rule for ListRule {
.with_label( .with_label(
Label::new((token.source(), token.range.clone())) Label::new((token.source(), token.range.clone()))
.with_message(err) .with_message(err)
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -398,12 +400,12 @@ impl Rule for ListRule {
.last_element::<ListEntry>() .last_element::<ListEntry>()
.map(|ent| ent.numbering.clone()) .map(|ent| ent.numbering.clone())
{ {
ListRule::push_markers(&token, state, document, &previous_depth, &depth); ListRule::push_markers(&token, parser, document, &previous_depth, &depth);
} else { } else {
ListRule::push_markers(&token, state, document, &vec![], &depth); ListRule::push_markers(&token, parser, document, &vec![], &depth);
} }
state.push( parser.push(
document, document,
Box::new(ListEntry { Box::new(ListEntry {
location: Token::new( location: Token::new(
@ -426,10 +428,13 @@ impl Rule for ListRule {
.map(|ent| ent.numbering.clone()) .map(|ent| ent.numbering.clone())
.unwrap(); .unwrap();
let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone()); let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone());
ListRule::push_markers(&token, state, document, &current, &Vec::new()); ListRule::push_markers(&token, parser, document, &current, &Vec::new());
(end_cursor, reports) (end_cursor, reports)
} }
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]
@ -438,7 +443,6 @@ mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -462,8 +466,7 @@ mod tests {
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let state = ParserState::new(&parser, None); let doc = parser.parse(source, None);
let (doc, _) = parser.parse(state, source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
ListMarker { numbered == false, kind == MarkerKind::Open }; ListMarker { numbered == false, kind == MarkerKind::Open };

View file

@ -7,6 +7,8 @@ use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Match; use regex::Match;
use regex::Regex; use regex::Regex;
@ -21,7 +23,7 @@ use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::element::ReferenceableElement; use crate::document::element::ReferenceableElement;
use crate::document::references::validate_refname; use crate::document::references::validate_refname;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -252,7 +254,7 @@ impl MediaRule {
.multi_line(true) .multi_line(true)
.build() .build()
.unwrap()], .unwrap()],
properties: PropertyParser { properties: props }, properties: PropertyParser{ properties: props },
} }
} }
@ -331,7 +333,7 @@ impl RegexRule for MediaRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -375,8 +377,7 @@ impl RegexRule for MediaRule {
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3)) let properties = match self.parse_properties(parser.colors(), &token, &matches.get(3)) {
{
Ok(pm) => pm, Ok(pm) => pm,
Err(report) => { Err(report) => {
reports.push(report); reports.push(report);
@ -400,10 +401,10 @@ impl RegexRule for MediaRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Property `type: {}` cannot be converted: {}", "Property `type: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(parser.colors().info),
err.fg(state.parser.colors().error) err.fg(parser.colors().error)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -419,7 +420,7 @@ impl RegexRule for MediaRule {
token.start() + 1..token.end(), token.start() + 1..token.end(),
)) ))
.with_message(format!("{err}. Required because mediatype could not be detected")) .with_message(format!("{err}. Required because mediatype could not be detected"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -453,7 +454,7 @@ impl RegexRule for MediaRule {
if source.content().is_empty() { if source.content().is_empty() {
None None
} else { } else {
match parse_paragraph(state, source, document) { match parse_paragraph(parser, source, document) {
Ok(paragraph) => Some(*paragraph), Ok(paragraph) => Some(*paragraph),
Err(err) => { Err(err) => {
reports.push( reports.push(
@ -464,7 +465,7 @@ impl RegexRule for MediaRule {
.with_message(format!( .with_message(format!(
"Could not parse description: {err}" "Could not parse description: {err}"
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -479,7 +480,7 @@ impl RegexRule for MediaRule {
let mut group = match document.last_element_mut::<Media>() { let mut group = match document.last_element_mut::<Media>() {
Some(group) => group, Some(group) => group,
None => { None => {
state.push( parser.push(
document, document,
Box::new(Media { Box::new(Media {
location: token.clone(), location: token.clone(),
@ -506,7 +507,7 @@ impl RegexRule for MediaRule {
.with_label( .with_label(
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(err) .with_message(err)
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -514,12 +515,13 @@ impl RegexRule for MediaRule {
reports reports
} }
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use super::*; use super::*;
@ -548,7 +550,7 @@ mod tests {
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
let borrow = doc.content().borrow(); let borrow = doc.content().borrow();
let group = borrow.first().as_ref().unwrap().as_container().unwrap(); let group = borrow.first().as_ref().unwrap().as_container().unwrap();

View file

@ -3,6 +3,8 @@ use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::Report; use ariadne::Report;
use mlua::Function;
use mlua::Lua;
use regex::Regex; use regex::Regex;
use crate::compiler::compiler::Compiler; use crate::compiler::compiler::Compiler;
@ -11,7 +13,7 @@ use crate::document::document::Document;
use crate::document::element::ContainerElement; use crate::document::element::ContainerElement;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::Rule; use crate::parser::rule::Rule;
use crate::parser::source::Cursor; use crate::parser::source::Cursor;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -106,7 +108,7 @@ impl ParagraphRule {
impl Rule for ParagraphRule { impl Rule for ParagraphRule {
fn name(&self) -> &'static str { "Paragraphing" } fn name(&self) -> &'static str { "Paragraphing" }
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.re self.re
.find_at(cursor.source.content(), cursor.pos) .find_at(cursor.source.content(), cursor.pos)
.and_then(|m| Some((m.start(), Box::new([false; 0]) as Box<dyn Any>))) .and_then(|m| Some((m.start(), Box::new([false; 0]) as Box<dyn Any>)))
@ -114,17 +116,17 @@ impl Rule for ParagraphRule {
fn on_match( fn on_match(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
cursor: Cursor, cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let end_cursor = match self.re.captures_at(cursor.source.content(), cursor.pos) { let end_cursor = match self.re.captures_at(cursor.source.content(), cursor.pos) {
None => panic!("Unknown error"), None => panic!("Unknown error"),
Some(capture) => cursor.at(capture.get(0).unwrap().end() - 1), Some(capture) => cursor.at(capture.get(0).unwrap().end() - 1),
}; };
state.push( parser.push(
document, document,
Box::new(Paragraph { Box::new(Paragraph {
location: Token::new(cursor.pos..end_cursor.pos, cursor.source.clone()), location: Token::new(cursor.pos..end_cursor.pos, cursor.source.clone()),
@ -134,6 +136,9 @@ impl Rule for ParagraphRule {
(end_cursor, Vec::new()) (end_cursor, Vec::new())
} }
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]
@ -141,7 +146,6 @@ mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -165,7 +169,7 @@ Last paragraph
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {

View file

@ -3,7 +3,7 @@ use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
@ -78,7 +78,7 @@ impl RegexRule for RawRule {
fn on_regex_match( fn on_regex_match(
&self, &self,
_index: usize, _index: usize,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -95,10 +95,10 @@ impl RegexRule for RawRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Missing terminating `{}` after first `{}`", "Missing terminating `{}` after first `{}`",
"?}".fg(state.parser.colors().info), "?}".fg(parser.colors().info),
"{?".fg(state.parser.colors().info) "{?".fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -115,7 +115,7 @@ impl RegexRule for RawRule {
.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(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -134,7 +134,7 @@ impl RegexRule for RawRule {
.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(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -152,7 +152,7 @@ impl RegexRule for RawRule {
.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(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -176,10 +176,10 @@ impl RegexRule for RawRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Property `kind: {}` cannot be converted: {}", "Property `kind: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(parser.colors().info),
err.fg(state.parser.colors().error) err.fg(parser.colors().error)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -196,9 +196,9 @@ impl RegexRule for RawRule {
)) ))
.with_message(format!( .with_message(format!(
"Property `{}` is missing", "Property `{}` is missing",
err.fg(state.parser.colors().info) err.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -207,7 +207,7 @@ impl RegexRule for RawRule {
}, },
}; };
state.push( parser.push(
document, document,
Box::new(Raw { Box::new(Raw {
location: token.clone(), location: token.clone(),
@ -219,7 +219,7 @@ impl RegexRule for RawRule {
reports reports
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
@ -242,7 +242,7 @@ impl RegexRule for RawRule {
CTX.with_borrow(|ctx| { CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Raw { Box::new(Raw {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -258,7 +258,7 @@ impl RegexRule for RawRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }
@ -268,8 +268,7 @@ mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser; use crate::parser::source::SourceFile;
use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
#[test] #[test]
@ -283,7 +282,7 @@ Break{?[kind=block] Raw?}NewParagraph{?<b>?}
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph; Paragraph;
@ -298,15 +297,15 @@ Break{?[kind=block] Raw?}NewParagraph{?<b>?}
#[test] #[test]
fn lua() { fn lua() {
let source = Rc::new(SourceFile::with_content( let source = Rc::new(SourceFile::with_content(
"".to_string(), "".to_string(),
r#" r#"
Break%<nml.raw.push("block", "Raw")>%NewParagraph%<nml.raw.push("inline", "<b>")>% Break%<nml.raw.push("block", "Raw")>%NewParagraph%<nml.raw.push("inline", "<b>")>%
"# "#
.to_string(), .to_string(),
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph; Paragraph;

View file

@ -6,6 +6,8 @@ use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Match; use regex::Match;
use regex::Regex; use regex::Regex;
@ -16,7 +18,7 @@ use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::references::validate_refname; use crate::document::references::validate_refname;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -80,7 +82,7 @@ impl ReferenceRule {
); );
Self { Self {
re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()], re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
properties: PropertyParser { properties: props }, properties: PropertyParser{ properties: props },
} }
} }
@ -133,7 +135,7 @@ impl RegexRule for ReferenceRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -153,7 +155,7 @@ impl RegexRule for ReferenceRule {
Label::new((token.source().clone(), m.range())).with_message( Label::new((token.source().clone(), m.range())).with_message(
format!( format!(
"Could not find element with reference: `{}`", "Could not find element with reference: `{}`",
refname.fg(state.parser.colors().info) refname.fg(parser.colors().info)
), ),
), ),
) )
@ -176,8 +178,7 @@ impl RegexRule for ReferenceRule {
} }
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3)) let properties = match self.parse_properties(parser.colors(), &token, &matches.get(3)) {
{
Ok(pm) => pm, Ok(pm) => pm,
Err(report) => { Err(report) => {
reports.push(report); reports.push(report);
@ -192,7 +193,7 @@ impl RegexRule for ReferenceRule {
.ok() .ok()
.and_then(|(_, s)| Some(s)); .and_then(|(_, s)| Some(s));
state.push( parser.push(
document, document,
Box::new(Reference { Box::new(Reference {
location: token, location: token,
@ -203,4 +204,6 @@ impl RegexRule for ReferenceRule {
reports reports
} }
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }

View file

@ -1,4 +1,5 @@
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::parser::ParserStrategy;
use super::code::CodeRule; use super::code::CodeRule;
use super::comment::CommentRule; use super::comment::CommentRule;

View file

@ -1,7 +1,7 @@
use crate::document::document::Document; use crate::document::document::Document;
use crate::lua::kernel::Kernel; use crate::lua::kernel::Kernel;
use crate::lua::kernel::KernelContext; use crate::lua::kernel::KernelContext;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -12,6 +12,7 @@ use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua; use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
@ -83,7 +84,7 @@ impl RegexRule for ScriptRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
index: usize, index: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -92,33 +93,26 @@ impl RegexRule for ScriptRule {
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(state.parser.colors(), name.as_str()) { Ok(name) => name,
Ok(name) => name, Err(e) => {
Err(e) => { reports.push(
reports.push( Report::build(ReportKind::Error, token.source(), name.start())
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(state.parser.colors().error), )
) .finish(),
.finish(), );
); return reports;
return reports;
}
} }
} },
};
let mut kernels_borrow = state.shared.kernels.borrow_mut();
let kernel = match kernels_borrow.get(kernel_name.as_str()) {
Some(kernel) => kernel,
None => {
kernels_borrow.insert(kernel_name.clone(), Kernel::new(state.parser));
kernels_borrow.get(kernel_name.as_str()).unwrap()
}
}; };
let kernel = parser
.get_kernel(kernel_name.as_str())
.unwrap_or_else(|| parser.insert_kernel(kernel_name.to_string(), Kernel::new(parser)));
let kernel_data = matches let kernel_data = matches
.get(if index == 0 { 2 } else { 3 }) .get(if index == 0 { 2 } else { 3 })
@ -133,7 +127,7 @@ impl RegexRule for ScriptRule {
.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(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -172,7 +166,7 @@ impl RegexRule for ScriptRule {
"Kernel execution failed:\n{}", "Kernel execution failed:\n{}",
e.to_string() e.to_string()
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -184,7 +178,7 @@ impl RegexRule for ScriptRule {
// Validate kind // Validate kind
let kind = match matches.get(2) { let kind = match matches.get(2) {
None => 0, None => 0,
Some(kind) => match self.validate_kind(state.parser.colors(), kind.as_str()) { Some(kind) => match self.validate_kind(parser.colors(), kind.as_str()) {
Ok(kind) => kind, Ok(kind) => kind,
Err(msg) => { Err(msg) => {
reports.push( reports.push(
@ -193,7 +187,7 @@ impl RegexRule for ScriptRule {
.with_label( .with_label(
Label::new((token.source(), kind.range())) Label::new((token.source(), kind.range()))
.with_message(msg) .with_message(msg)
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -215,7 +209,7 @@ impl RegexRule for ScriptRule {
"Kernel evaluation failed:\n{}", "Kernel evaluation failed:\n{}",
e.to_string() e.to_string()
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -229,7 +223,7 @@ impl RegexRule for ScriptRule {
// Eval to text // Eval to text
{ {
if !result.is_empty() { if !result.is_empty() {
state.push( parser.push(
document, document,
Box::new(Text::new( Box::new(Text::new(
Token::new(1..source.content().len(), source.clone()), Token::new(1..source.content().len(), source.clone()),
@ -246,11 +240,7 @@ impl RegexRule for ScriptRule {
result, result,
)) as Rc<dyn Source>; )) as Rc<dyn Source>;
state.with_state(|new_state| { parser.parse_into(parse_source, document);
new_state
.parser
.parse_into(new_state, parse_source, document);
})
} }
} }
Err(e) => { Err(e) => {
@ -263,7 +253,7 @@ impl RegexRule for ScriptRule {
"Kernel evaluation failed:\n{}", "Kernel evaluation failed:\n{}",
e.to_string() e.to_string()
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -277,12 +267,15 @@ 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()),
state, parser,
document, document,
}; };
kernel.run_with_context(ctx, execute) kernel.run_with_context(ctx, execute)
} }
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]
@ -294,7 +287,6 @@ mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::elements::style::Style; use crate::elements::style::Style;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -320,7 +312,7 @@ Evaluation: %<! make_ref("hello", "id")>%
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph; Paragraph;

View file

@ -5,11 +5,10 @@ use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::element::ReferenceableElement; use crate::document::element::ReferenceableElement;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::style::StyleHolder;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
@ -161,7 +160,7 @@ impl RegexRule for SectionRule {
fn on_regex_match( fn on_regex_match(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
@ -176,9 +175,9 @@ impl RegexRule for SectionRule {
.with_label( .with_label(
Label::new((token.source(), depth.range())) Label::new((token.source(), depth.range()))
.with_message(format!("Section is of depth {}, which is greather than {} (maximum depth allowed)", .with_message(format!("Section is of depth {}, which is greather than {} (maximum depth allowed)",
depth.len().fg(state.parser.colors().info), depth.len().fg(parser.colors().info),
6.fg(state.parser.colors().info))) 6.fg(parser.colors().info)))
.with_color(state.parser.colors().error)) .with_color(parser.colors().error))
.finish()); .finish());
return result; return result;
} }
@ -203,17 +202,17 @@ impl RegexRule for SectionRule {
.with_label( .with_label(
Label::new((token.source(), refname.range())) Label::new((token.source(), refname.range()))
.with_message(format!("Reference with name `{}` is already defined in `{}`", .with_message(format!("Reference with name `{}` is already defined in `{}`",
refname.as_str().fg(state.parser.colors().highlight), refname.as_str().fg(parser.colors().highlight),
elem.location().source().name().as_str().fg(state.parser.colors().highlight))) elem.location().source().name().as_str().fg(parser.colors().highlight)))
.with_message(format!("`{}` conflicts with previously defined reference to {}", .with_message(format!("`{}` conflicts with previously defined reference to {}",
refname.as_str().fg(state.parser.colors().highlight), refname.as_str().fg(parser.colors().highlight),
elem.element_name().fg(state.parser.colors().highlight))) elem.element_name().fg(parser.colors().highlight)))
.with_color(state.parser.colors().warning)) .with_color(parser.colors().warning))
.with_label( .with_label(
Label::new((elem.location().source(), elem.location().start()..elem.location().end() )) Label::new((elem.location().source(), elem.location().start()..elem.location().end() ))
.with_message(format!("`{}` previously defined here", .with_message(format!("`{}` previously defined here",
refname.as_str().fg(state.parser.colors().highlight))) refname.as_str().fg(parser.colors().highlight)))
.with_color(state.parser.colors().warning)) .with_color(parser.colors().warning))
.with_note(format!("Previous reference was overwritten")) .with_note(format!("Previous reference was overwritten"))
.finish()); .finish());
} }
@ -235,10 +234,10 @@ impl RegexRule for SectionRule {
.with_label( .with_label(
Label::new((token.source(), kind.range())) Label::new((token.source(), kind.range()))
.with_message(format!("Section numbering kind must be a combination of `{}` for unnumbered, and `{}` for non-listing; got `{}`", .with_message(format!("Section numbering kind must be a combination of `{}` for unnumbered, and `{}` for non-listing; got `{}`",
"*".fg(state.parser.colors().info), "*".fg(parser.colors().info),
"+".fg(state.parser.colors().info), "+".fg(parser.colors().info),
kind.as_str().fg(state.parser.colors().highlight))) kind.as_str().fg(parser.colors().highlight)))
.with_color(state.parser.colors().error)) .with_color(parser.colors().error))
.with_help(format!("Leave empty for a numbered listed section")) .with_help(format!("Leave empty for a numbered listed section"))
.finish()); .finish());
return result; return result;
@ -266,7 +265,7 @@ impl RegexRule for SectionRule {
.with_label( .with_label(
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message("Sections require a name before line end") .with_message("Sections require a name before line end")
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -281,8 +280,8 @@ impl RegexRule for SectionRule {
.with_label( .with_label(
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message("Sections require at least one whitespace before the section's name") .with_message("Sections require at least one whitespace before the section's name")
.with_color(state.parser.colors().warning)) .with_color(parser.colors().warning))
.with_help(format!("Add a space before `{}`", section_name.fg(state.parser.colors().highlight))) .with_help(format!("Add a space before `{}`", section_name.fg(parser.colors().highlight)))
.finish()); .finish());
return result; return result;
} }
@ -293,15 +292,12 @@ impl RegexRule for SectionRule {
}; };
// Get style // Get style
let style = state let style = parser
.shared .current_style(section_style::STYLE_KEY)
.styles
.borrow()
.current(section_style::STYLE_KEY)
.downcast_rc::<SectionStyle>() .downcast_rc::<SectionStyle>()
.unwrap(); .unwrap();
state.push( parser.push(
document, document,
Box::new(Section { Box::new(Section {
location: token.clone(), location: token.clone(),
@ -316,7 +312,7 @@ impl RegexRule for SectionRule {
return result; return result;
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
@ -344,15 +340,12 @@ impl RegexRule for SectionRule {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
// Get style // Get style
let style = ctx let style = ctx
.state .parser
.shared .current_style(section_style::STYLE_KEY)
.styles
.borrow()
.current(section_style::STYLE_KEY)
.downcast_rc::<SectionStyle>() .downcast_rc::<SectionStyle>()
.unwrap(); .unwrap();
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Section { Box::new(Section {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -372,11 +365,11 @@ impl RegexRule for SectionRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
fn register_styles(&self, holder: &mut StyleHolder) { fn register_styles(&self, parser: &dyn Parser) {
holder.set_current(Rc::new(SectionStyle::default())); parser.set_current_style(Rc::new(SectionStyle::default()));
} }
} }
@ -384,6 +377,7 @@ mod section_style {
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::document::style::ElementStyle;
use crate::impl_elementstyle; use crate::impl_elementstyle;
pub static STYLE_KEY: &'static str = "style.section"; pub static STYLE_KEY: &'static str = "style.section";
@ -415,8 +409,8 @@ mod section_style {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::document::style::StyleHolder;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -438,7 +432,7 @@ mod tests {
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Section { depth == 1, title == "1" }; Section { depth == 1, title == "1" };
@ -468,7 +462,7 @@ nml.section.push("6", 6, "", "refname")
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Section { depth == 1, title == "1" }; Section { depth == 1, title == "1" };
@ -494,13 +488,10 @@ nml.section.push("6", 6, "", "refname")
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let state = ParserState::new(&parser, None); let _ = parser.parse(source, None);
let (_, state) = parser.parse(state, source, None);
let style = state.shared let style = parser
.styles .current_style(section_style::STYLE_KEY)
.borrow()
.current(section_style::STYLE_KEY)
.downcast_rc::<SectionStyle>() .downcast_rc::<SectionStyle>()
.unwrap(); .unwrap();

View file

@ -4,16 +4,19 @@ use crate::document::document::Document;
use crate::document::document::DocumentAccessors; use crate::document::document::DocumentAccessors;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::state::RuleState;
use crate::parser::state::Scope; use crate::parser::state::Scope;
use crate::parser::state::State;
use ariadne::Fmt; use ariadne::Fmt;
use ariadne::Label; use ariadne::Label;
use ariadne::Report; use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use lazy_static::lazy_static;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
use std::cell::RefCell; use std::cell::RefCell;
@ -74,12 +77,12 @@ impl StyleState {
} }
} }
impl RuleState for StyleState { impl State for StyleState {
fn scope(&self) -> Scope { Scope::PARAGRAPH } fn scope(&self) -> Scope { Scope::PARAGRAPH }
fn on_remove<'a>( fn on_remove<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut reports = vec![];
@ -113,15 +116,15 @@ impl RuleState for StyleState {
.with_order(1) .with_order(1)
.with_message(format!( .with_message(format!(
"Style {} starts here", "Style {} starts here",
name.fg(state.parser.colors().info) name.fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_label( .with_label(
Label::new(paragraph_end) Label::new(paragraph_end)
.with_order(1) .with_order(1)
.with_message(format!("Paragraph ends here")) .with_message(format!("Paragraph ends here"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_note("Styles cannot span multiple documents (i.e @import)") .with_note("Styles cannot span multiple documents (i.e @import)")
.finish(), .finish(),
@ -153,7 +156,9 @@ impl StyleRule {
} }
} }
static STATE_NAME: &'static str = "elements.style"; lazy_static! {
static ref STATE_NAME: String = "elements.style".to_string();
}
impl RegexRule for StyleRule { impl RegexRule for StyleRule {
fn name(&self) -> &'static str { "Style" } fn name(&self) -> &'static str { "Style" }
@ -163,21 +168,19 @@ impl RegexRule for StyleRule {
fn on_regex_match( fn on_regex_match(
&self, &self,
index: usize, index: usize,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
_matches: Captures, _matches: Captures,
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
let query = state.shared.rule_state.borrow().get(STATE_NAME); let query = parser.state().query(&STATE_NAME);
let style_state = match query { let state = match query {
Some(state) => state, Some(state) => state,
None => { None => {
// Insert as a new state // Insert as a new state
match state match parser
.shared .state_mut()
.rule_state .insert(STATE_NAME.clone(), Rc::new(RefCell::new(StyleState::new())))
.borrow_mut()
.insert(STATE_NAME.into(), Rc::new(RefCell::new(StyleState::new())))
{ {
Err(_) => panic!("Unknown error"), Err(_) => panic!("Unknown error"),
Ok(state) => state, Ok(state) => state,
@ -185,11 +188,11 @@ impl RegexRule for StyleRule {
} }
}; };
if let Some(style_state) = style_state.borrow_mut().downcast_mut::<StyleState>() { if let Some(style_state) = state.borrow_mut().downcast_mut::<StyleState>() {
style_state.toggled[index] = style_state.toggled[index] style_state.toggled[index] = style_state.toggled[index]
.clone() .clone()
.map_or(Some(token.clone()), |_| None); .map_or(Some(token.clone()), |_| None);
state.push( parser.push(
document, document,
Box::new(Style::new( Box::new(Style::new(
token.clone(), token.clone(),
@ -198,18 +201,20 @@ impl RegexRule for StyleRule {
)), )),
); );
} else { } else {
panic!("Invalid state at `{STATE_NAME}`"); panic!("Invalid state at `{}`", STATE_NAME.as_str());
} }
return vec![]; return vec![];
} }
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -230,7 +235,7 @@ __`UNDERLINE+EM`__
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {

View file

@ -14,6 +14,8 @@ use ariadne::Report;
use ariadne::ReportKind; use ariadne::ReportKind;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2::Sha512; use crypto::sha2::Sha512;
use mlua::Function;
use mlua::Lua;
use regex::Captures; use regex::Captures;
use regex::Match; use regex::Match;
use regex::Regex; use regex::Regex;
@ -25,7 +27,7 @@ use crate::compiler::compiler::Target;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -302,7 +304,7 @@ impl RegexRule for TexRule {
fn on_regex_match( fn on_regex_match(
&self, &self,
index: usize, index: usize,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
token: Token, token: Token,
matches: Captures, matches: Captures,
@ -319,10 +321,10 @@ impl RegexRule for TexRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Missing terminating `{}` after first `{}`", "Missing terminating `{}` after first `{}`",
["|$", "$"][index].fg(state.parser.colors().info), ["|$", "$"][index].fg(parser.colors().info),
["$|", "$"][index].fg(state.parser.colors().info) ["$|", "$"][index].fg(parser.colors().info)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -342,7 +344,7 @@ impl RegexRule for TexRule {
.with_label( .with_label(
Label::new((token.source().clone(), content.range())) Label::new((token.source().clone(), content.range()))
.with_message("Tex code is empty") .with_message("Tex code is empty")
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -352,8 +354,7 @@ impl RegexRule for TexRule {
}; };
// Properties // Properties
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(1)) let properties = match self.parse_properties(parser.colors(), &token, &matches.get(1)) {
{
Ok(pm) => pm, Ok(pm) => pm,
Err(report) => { Err(report) => {
reports.push(report); reports.push(report);
@ -375,10 +376,10 @@ impl RegexRule for TexRule {
Label::new((token.source().clone(), token.range.clone())) Label::new((token.source().clone(), token.range.clone()))
.with_message(format!( .with_message(format!(
"Property `kind: {}` cannot be converted: {}", "Property `kind: {}` cannot be converted: {}",
prop.fg(state.parser.colors().info), prop.fg(parser.colors().info),
err.fg(state.parser.colors().error) err.fg(parser.colors().error)
)) ))
.with_color(state.parser.colors().warning), .with_color(parser.colors().warning),
) )
.finish(), .finish(),
); );
@ -411,7 +412,7 @@ impl RegexRule for TexRule {
.and_then(|(_, value)| Some(value)) .and_then(|(_, value)| Some(value))
.unwrap(); .unwrap();
state.push( parser.push(
document, document,
Box::new(Tex { Box::new(Tex {
mathmode: index == 1, mathmode: index == 1,
@ -425,13 +426,15 @@ impl RegexRule for TexRule {
reports reports
} }
// TODO
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::parser::Parser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -450,7 +453,7 @@ $[kind=block,env=another] e^{i\pi}=-1$
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) }; Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) };
@ -472,7 +475,7 @@ $[env=another] e^{i\pi}=-1$
None, None,
)); ));
let parser = LangParser::default(); let parser = LangParser::default();
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None); let doc = parser.parse(source, None);
validate_document!(doc.content().borrow(), 0, validate_document!(doc.content().borrow(), 0,
Paragraph { Paragraph {

View file

@ -11,7 +11,7 @@ use crate::document::document::Document;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::rule::Rule; use crate::parser::rule::Rule;
use crate::parser::source::Cursor; use crate::parser::source::Cursor;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -48,26 +48,26 @@ 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, _state: &ParserState, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None } fn next_match(&self, _parser: &dyn Parser, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None }
fn on_match( fn on_match(
&self, &self,
_state: &ParserState, _parser: &dyn Parser,
_document: &dyn Document, _document: &dyn Document,
_cursor: Cursor, _cursor: Cursor,
_match_data: Box<dyn Any>, _match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
panic!("Text cannot match"); panic!("Text cannot match");
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
"push".to_string(), "push".to_string(),
lua.create_function(|_, content: String| { lua.create_function(|_, content: String| {
CTX.with_borrow(|ctx| { CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
ctx.state.push( ctx.parser.push(
ctx.document, ctx.document,
Box::new(Text { Box::new(Text {
location: ctx.location.clone(), location: ctx.location.clone(),
@ -82,6 +82,6 @@ impl Rule for TextRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }

View file

@ -3,7 +3,7 @@ use crate::document::variable::BaseVariable;
use crate::document::variable::PathVariable; use crate::document::variable::PathVariable;
use crate::document::variable::Variable; use crate::document::variable::Variable;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::ParserState; use crate::parser::parser::Parser;
use crate::parser::parser::ReportColors; use crate::parser::parser::ReportColors;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::source::Source; use crate::parser::source::Source;
@ -123,7 +123,7 @@ impl RegexRule for VariableRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_: usize, _: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document, document: &'a dyn Document,
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
@ -148,9 +148,9 @@ impl RegexRule for VariableRule {
Label::new((token.source(), kind.range())) Label::new((token.source(), kind.range()))
.with_message(format!( .with_message(format!(
"Variable kind `{}` is unknown", "Variable kind `{}` is unknown",
kind.as_str().fg(state.parser.colors().highlight) kind.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_help(format!( .with_help(format!(
"Leave empty for regular variables. Available variable kinds:{}", "Leave empty for regular variables. Available variable kinds:{}",
@ -159,8 +159,8 @@ impl RegexRule for VariableRule {
|acc, (char, name)| { |acc, (char, name)| {
acc + format!( acc + format!(
"\n - `{}` : {}", "\n - `{}` : {}",
char.fg(state.parser.colors().highlight), char.fg(parser.colors().highlight),
name.fg(state.parser.colors().info) name.fg(parser.colors().info)
) )
.as_str() .as_str()
} }
@ -178,7 +178,7 @@ impl RegexRule for VariableRule {
}; };
let var_name = match matches.get(2) { let var_name = match matches.get(2) {
Some(name) => match VariableRule::validate_name(&state.parser.colors(), name.as_str()) { Some(name) => match VariableRule::validate_name(&parser.colors(), name.as_str()) {
Ok(var_name) => var_name, Ok(var_name) => var_name,
Err(msg) => { Err(msg) => {
result.push( result.push(
@ -188,9 +188,9 @@ impl RegexRule for VariableRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Variable name `{}` is not allowed. {msg}", "Variable name `{}` is not allowed. {msg}",
name.as_str().fg(state.parser.colors().highlight) name.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -212,9 +212,9 @@ impl RegexRule for VariableRule {
Label::new((token.source(), value.range())) Label::new((token.source(), value.range()))
.with_message(format!( .with_message(format!(
"Variable value `{}` is not allowed. {msg}", "Variable value `{}` is not allowed. {msg}",
value.as_str().fg(state.parser.colors().highlight) value.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -226,7 +226,7 @@ impl RegexRule for VariableRule {
}; };
match self.make_variable( match self.make_variable(
&state.parser.colors(), &parser.colors(),
token.clone(), token.clone(),
var_kind, var_kind,
var_name.to_string(), var_name.to_string(),
@ -242,10 +242,10 @@ impl RegexRule for VariableRule {
Label::new((token.source(), m.start() + 1..m.end())) Label::new((token.source(), m.start() + 1..m.end()))
.with_message(format!( .with_message(format!(
"Unable to create variable `{}`. {}", "Unable to create variable `{}`. {}",
var_name.fg(state.parser.colors().highlight), var_name.fg(parser.colors().highlight),
msg msg
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -257,7 +257,7 @@ impl RegexRule for VariableRule {
return result; return result;
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![]; let mut bindings = vec![];
bindings.push(( bindings.push((
"insert".to_string(), "insert".to_string(),
@ -291,7 +291,7 @@ impl RegexRule for VariableRule {
.unwrap(), .unwrap(),
)); ));
bindings Some(bindings)
} }
} }
@ -315,7 +315,7 @@ impl RegexRule for VariableSubstitutionRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
_index: usize, _index: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
@ -332,7 +332,7 @@ impl RegexRule for VariableSubstitutionRule {
.with_label( .with_label(
Label::new((token.source(), matches.get(0).unwrap().range())) Label::new((token.source(), matches.get(0).unwrap().range()))
.with_message(format!("Missing variable name for substitution")) .with_message(format!("Missing variable name for substitution"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -347,7 +347,7 @@ impl RegexRule for VariableSubstitutionRule {
.with_label( .with_label(
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!("Variable names contains leading spaces")) .with_message(format!("Variable names contains leading spaces"))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_help("Remove leading spaces") .with_help("Remove leading spaces")
.finish(), .finish(),
@ -365,7 +365,7 @@ impl RegexRule for VariableSubstitutionRule {
.with_message(format!( .with_message(format!(
"Variable names contains trailing spaces" "Variable names contains trailing spaces"
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.with_help("Remove trailing spaces") .with_help("Remove trailing spaces")
.finish(), .finish(),
@ -374,7 +374,7 @@ impl RegexRule for VariableSubstitutionRule {
return result; return result;
} }
// Invalid name // Invalid name
match VariableRule::validate_name(&state.parser.colors(), name.as_str()) { match VariableRule::validate_name(&parser.colors(), name.as_str()) {
Err(msg) => { Err(msg) => {
result.push( result.push(
Report::build(ReportKind::Error, token.source(), name.start()) Report::build(ReportKind::Error, token.source(), name.start())
@ -382,7 +382,7 @@ impl RegexRule for VariableSubstitutionRule {
.with_label( .with_label(
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(msg) .with_message(msg)
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -402,9 +402,9 @@ impl RegexRule for VariableSubstitutionRule {
Label::new((token.source(), name.range())) Label::new((token.source(), name.range()))
.with_message(format!( .with_message(format!(
"Unable to find variable with name: `{}`", "Unable to find variable with name: `{}`",
name.as_str().fg(state.parser.colors().highlight) name.as_str().fg(parser.colors().highlight)
)) ))
.with_color(state.parser.colors().error), .with_color(parser.colors().error),
) )
.finish(), .finish(),
); );
@ -416,8 +416,10 @@ impl RegexRule for VariableSubstitutionRule {
_ => panic!("Unknown error"), _ => panic!("Unknown error"),
}; };
variable.parse(state, token, document); variable.parse(token, parser, document);
return result; return result;
} }
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }

View file

@ -1,21 +1,25 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::cell::RefMut;
use mlua::Error;
use mlua::FromLuaMulti;
use mlua::Function;
use mlua::IntoLuaMulti;
use mlua::Lua; use mlua::Lua;
use crate::document::document::Document; use crate::document::document::Document;
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::parser::ParserState;
use crate::parser::source::Token; use crate::parser::source::Token;
pub struct KernelContext<'a, 'b, 'c> { pub struct KernelContext<'a, 'b> {
pub location: Token, pub location: Token,
pub state: &'a ParserState<'a, 'b>, pub parser: &'a dyn Parser,
pub document: &'c dyn Document<'c>, pub document: &'b dyn Document<'b>,
//pub parser: &'a dyn Parser,
} }
thread_local! { thread_local! {
pub static CTX: RefCell<Option<KernelContext<'static, 'static, 'static>>> = RefCell::new(None); pub static CTX: RefCell<Option<KernelContext<'static, 'static>>> = RefCell::new(None);
} }
#[derive(Debug)] #[derive(Debug)]
@ -24,6 +28,9 @@ pub struct Kernel {
} }
impl Kernel { impl Kernel {
// TODO: Take parser as arg and
// iterate over the rules
// to find export the bindings (if some)
pub fn new(parser: &dyn Parser) -> Self { pub fn new(parser: &dyn Parser) -> Self {
let lua = Lua::new(); let lua = Lua::new();
@ -31,13 +38,16 @@ impl Kernel {
let nml_table = lua.create_table().unwrap(); let nml_table = lua.create_table().unwrap();
for rule in parser.rules() { for rule in parser.rules() {
let table = lua.create_table().unwrap(); if let Some(bindings) = rule.lua_bindings(&lua) {
// TODO: Export this so we can check for duplicate rules based on this name let table = lua.create_table().unwrap();
let name = rule.name().to_lowercase().replace(' ', "_"); let name = rule.name().to_lowercase().replace(' ', "_");
for (fun_name, fun) in rule.register_bindings(&lua) {
table.set(fun_name, fun).unwrap(); for (fun_name, fun) in bindings {
table.set(fun_name, fun).unwrap();
}
nml_table.set(name, table).unwrap();
} }
nml_table.set(name, table).unwrap();
} }
lua.globals().set("nml", nml_table).unwrap(); lua.globals().set("nml", nml_table).unwrap();
} }
@ -61,15 +71,8 @@ impl Kernel {
} }
} }
#[derive(Default)] pub trait KernelHolder {
pub struct KernelHolder { fn get_kernel(&self, name: &str) -> Option<RefMut<'_, Kernel>>;
kernels: HashMap<String, Kernel>,
}
impl KernelHolder { fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>;
pub fn get(&self, kernel_name: &str) -> Option<&Kernel> { self.kernels.get(kernel_name) }
pub fn insert(&mut self, kernel_name: String, kernel: Kernel) {
self.kernels.insert(kernel_name, kernel);
}
} }

View file

@ -21,7 +21,6 @@ use document::document::Document;
use getopts::Options; use getopts::Options;
use parser::langparser::LangParser; use parser::langparser::LangParser;
use parser::parser::Parser; use parser::parser::Parser;
use parser::parser::ParserState;
use rusqlite::Connection; use rusqlite::Connection;
use walkdir::WalkDir; use walkdir::WalkDir;
@ -47,16 +46,13 @@ NML version: 0.4\n"
); );
} }
fn parse( fn parse(input: &str, debug_opts: &Vec<String>) -> Result<Box<dyn Document<'static>>, String> {
parser: &LangParser,
input: &str,
debug_opts: &Vec<String>,
) -> Result<Box<dyn Document<'static>>, String> {
println!("Parsing {input}..."); println!("Parsing {input}...");
let parser = LangParser::default();
// Parse // Parse
let source = SourceFile::new(input.to_string(), None).unwrap(); let source = SourceFile::new(input.to_string(), None).unwrap();
let (doc, _) = parser.parse(ParserState::new(parser, None), Rc::new(source), None); let doc = parser.parse(Rc::new(source), None);
if debug_opts.contains(&"ast".to_string()) { if debug_opts.contains(&"ast".to_string()) {
println!("-- BEGIN AST DEBUGGING --"); println!("-- BEGIN AST DEBUGGING --");
@ -110,7 +106,6 @@ fn process(
CompiledDocument::init_cache(&con) CompiledDocument::init_cache(&con)
.map_err(|err| format!("Failed to initialize cached document table: {err}"))?; .map_err(|err| format!("Failed to initialize cached document table: {err}"))?;
let parser = LangParser::default();
for file in files { for file in files {
let meta = std::fs::metadata(&file) let meta = std::fs::metadata(&file)
.map_err(|err| format!("Failed to get metadata for `{file:#?}`: {err}"))?; .map_err(|err| format!("Failed to get metadata for `{file:#?}`: {err}"))?;
@ -128,7 +123,7 @@ fn process(
let parse_and_compile = || -> Result<CompiledDocument, String> { let parse_and_compile = || -> Result<CompiledDocument, String> {
// Parse // Parse
let doc = parse(&parser, file.to_str().unwrap(), debug_opts)?; let doc = parse(file.to_str().unwrap(), debug_opts)?;
// Compile // Compile
let compiler = Compiler::new(target, db_path.clone()); let compiler = Compiler::new(target, db_path.clone());
@ -221,7 +216,8 @@ fn main() -> ExitCode {
} }
match std::fs::metadata(&output) { match std::fs::metadata(&output) {
Ok(output_meta) => { Ok(output_meta) => {
if !output_meta.is_dir() { if !output_meta.is_dir()
{
eprintln!("Input is a directory, but ouput is not a directory, halting"); eprintln!("Input is a directory, but ouput is not a directory, halting");
return ExitCode::FAILURE; return ExitCode::FAILURE;
} }

View file

@ -1,63 +0,0 @@
use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
use std::ops::Deref;
use ariadne::Report;
use crate::document::document::Document;
use crate::parser::source::Source;
use crate::parser::source::Token;
use super::parser::ParserState;
#[derive(Debug, PartialEq, Eq)]
pub enum CustomStyleToken {
Toggle(String),
Pair(String, String),
}
pub trait CustomStyle: core::fmt::Debug {
/// Name for the custom style
fn name(&self) -> &str;
/// Gets the begin and end token for a custom style
fn tokens(&self) -> &CustomStyleToken;
fn on_start<'a>(
&self,
location: Token,
state: &ParserState,
document: &'a (dyn Document<'a> + 'a),
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>>;
fn on_end<'a>(
&self,
location: Token,
state: &ParserState,
document: &'a (dyn Document<'a> + 'a),
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>>;
}
#[derive(Default)]
pub struct CustomStyleHolder {
custom_styles: HashMap<String, Rc<dyn CustomStyle>>,
}
impl CustomStyleHolder {
pub fn get(&self, style_name: &str) -> Option<Rc<dyn CustomStyle>> {
self.custom_styles
.get(style_name)
.map(|style| style.clone())
}
pub fn insert(&mut self, style: Rc<dyn CustomStyle>) {
self.custom_styles.insert(style.name().into(), style);
}
}
impl Deref for CustomStyleHolder {
type Target = HashMap<String, Rc<dyn CustomStyle>>;
fn deref(&self) -> &Self::Target {
&self.custom_styles
}
}

View file

@ -1,19 +1,44 @@
use std::any::Any;
use std::cell::Ref;
use std::cell::RefCell; use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::HashMap;
use std::collections::HashSet;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::Label;
use ariadne::Report;
use crate::document::customstyle::CustomStyle;
use crate::document::customstyle::CustomStyleHolder;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::document::DocumentAccessors;
use crate::document::element::ContainerElement;
use crate::document::element::DocumentEnd; use crate::document::element::DocumentEnd;
use crate::document::element::ElemKind;
use crate::document::element::Element;
use crate::document::langdocument::LangDocument; use crate::document::langdocument::LangDocument;
use crate::document::layout::LayoutHolder;
use crate::document::layout::LayoutType;
use crate::document::style::ElementStyle;
use crate::document::style::StyleHolder;
use crate::elements::paragraph::Paragraph;
use crate::elements::registrar::register; use crate::elements::registrar::register;
use crate::elements::text::Text; use crate::elements::text::Text;
use crate::lua::kernel::Kernel;
use crate::lua::kernel::KernelHolder;
use crate::parser::source::SourceFile;
use crate::parser::source::VirtualSource;
use super::parser::Parser; use super::parser::Parser;
use super::parser::ParserState; use super::parser::ParserStrategy;
use super::parser::ReportColors; use super::parser::ReportColors;
use super::rule::Rule; use super::rule::Rule;
use super::source::Cursor; use super::source::Cursor;
use super::source::Source; use super::source::Source;
use super::source::Token; use super::source::Token;
use super::state::StateHolder;
use super::util; use super::util;
/// Parser for the language /// Parser for the language
@ -24,6 +49,13 @@ pub struct LangParser {
// Parser state // Parser state
pub err_flag: RefCell<bool>, pub err_flag: RefCell<bool>,
pub matches: RefCell<Vec<(usize, Option<Box<dyn Any>>)>>,
pub state: RefCell<StateHolder>,
pub kernels: RefCell<HashMap<String, Kernel>>,
pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>,
pub layouts: RefCell<HashMap<String, Rc<dyn LayoutType>>>,
pub custom_styles: RefCell<HashMap<String, Rc<dyn CustomStyle>>>,
} }
impl LangParser { impl LangParser {
@ -32,14 +64,90 @@ impl LangParser {
rules: vec![], rules: vec![],
colors: ReportColors::with_colors(), colors: ReportColors::with_colors(),
err_flag: RefCell::new(false), err_flag: RefCell::new(false),
matches: RefCell::new(Vec::new()),
state: RefCell::new(StateHolder::new()),
kernels: RefCell::new(HashMap::new()),
styles: RefCell::new(HashMap::new()),
layouts: RefCell::new(HashMap::new()),
custom_styles: RefCell::new(HashMap::new()),
}; };
// Register rules // Register rules
// TODO: use https://docs.rs/inventory/latest/inventory/
register(&mut s); register(&mut s);
// Register default kernel
s.kernels
.borrow_mut()
.insert("main".to_string(), Kernel::new(&s));
// Register default styles
for rule in &s.rules {
rule.register_styles(&s);
}
// Register default layouts
for rule in &s.rules {
rule.register_layouts(&s);
}
s s
} }
fn handle_reports<'a>(
&self,
_source: Rc<dyn Source>,
reports: Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>>,
) {
for mut report in reports {
let mut sources: HashSet<Rc<dyn Source>> = HashSet::new();
fn recurse_source(sources: &mut HashSet<Rc<dyn Source>>, source: Rc<dyn Source>) {
sources.insert(source.clone());
match source.location() {
Some(parent) => {
let parent_source = parent.source();
if sources.get(&parent_source).is_none() {
recurse_source(sources, parent_source);
}
}
None => {}
}
}
report.labels.iter().for_each(|label| {
recurse_source(&mut sources, label.span.0.clone());
});
let cache = sources
.iter()
.map(|source| (source.clone(), source.content().clone()))
.collect::<Vec<(Rc<dyn Source>, String)>>();
cache.iter().for_each(|(source, _)| {
if let Some(location) = source.location() {
if let Some(_s) = source.downcast_ref::<SourceFile>() {
report.labels.push(
Label::new((location.source(), location.start() + 1..location.end()))
.with_message("In file included from here")
.with_order(-1),
);
};
if let Some(_s) = source.downcast_ref::<VirtualSource>() {
let start = location.start()
+ (location.source().content().as_bytes()[location.start()]
== '\n' as u8)
.then_some(1)
.unwrap_or(0);
report.labels.push(
Label::new((location.source(), start..location.end()))
.with_message("In evaluation of")
.with_order(-1),
);
};
}
});
report.eprint(ariadne::sources(cache)).unwrap()
}
}
} }
impl Parser for LangParser { impl Parser for LangParser {
@ -48,15 +156,50 @@ impl Parser for LangParser {
fn rules(&self) -> &Vec<Box<dyn Rule>> { &self.rules } fn rules(&self) -> &Vec<Box<dyn Rule>> { &self.rules }
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>> { &mut self.rules } fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>> { &mut self.rules }
fn state(&self) -> std::cell::Ref<'_, StateHolder> { self.state.borrow() }
fn state_mut(&self) -> std::cell::RefMut<'_, StateHolder> { self.state.borrow_mut() }
fn has_error(&self) -> bool { *self.err_flag.borrow() } fn has_error(&self) -> bool { *self.err_flag.borrow() }
fn parse<'p, 'a, 'doc>( /// Add an [`Element`] to the [`Document`]
&'p self, fn push<'a>(&self, doc: &dyn Document, elem: Box<dyn Element>) {
state: ParserState<'p, 'a>, if elem.kind() == ElemKind::Inline || elem.kind() == ElemKind::Invisible {
let mut paragraph = doc
.last_element_mut::<Paragraph>()
.or_else(|| {
doc.push(Box::new(Paragraph {
location: elem.location().clone(),
content: Vec::new(),
}));
doc.last_element_mut::<Paragraph>()
})
.unwrap();
paragraph.push(elem).unwrap();
} else {
// Process paragraph events
if doc.last_element::<Paragraph>().is_some_and(|_| true) {
self.handle_reports(
doc.source(),
self.state_mut()
.on_scope_end(self, doc, super::state::Scope::PARAGRAPH),
);
}
doc.push(elem);
}
}
fn parse<'a>(
&self,
source: Rc<dyn Source>, source: Rc<dyn Source>,
parent: Option<&'doc dyn Document<'doc>>, parent: Option<&'a dyn Document<'a>>,
) -> (Box<dyn Document<'doc> + 'doc>, ParserState<'p, 'a>) { ) -> Box<dyn Document<'a> + 'a> {
let doc = LangDocument::new(source.clone(), parent); let doc = LangDocument::new(source.clone(), parent);
let mut matches = Vec::new();
for _ in 0..self.rules.len() {
matches.push((0usize, None));
}
let content = source.content(); let content = source.content();
let mut cursor = Cursor::new(0usize, doc.source()); // Cursor in file let mut cursor = Cursor::new(0usize, doc.source()); // Cursor in file
@ -64,21 +207,21 @@ impl Parser for LangParser {
if let Some(parent) = parent if let Some(parent) = parent
// Terminate parent's paragraph state // Terminate parent's paragraph state
{ {
self.handle_reports(state.shared.rule_state.borrow_mut().on_scope_end( self.handle_reports(
&state, parent.source(),
parent, self.state_mut()
super::state::Scope::PARAGRAPH, .on_scope_end(self, parent, super::state::Scope::PARAGRAPH),
)); );
} }
loop { loop {
let (rule_pos, mut result) = state.update_matches(&cursor); let (rule_pos, rule, match_data) = self.update_matches(&cursor, &mut matches);
// Unmatched content // Unmatched content
let text_content = let text_content =
util::process_text(&doc, &content.as_str()[cursor.pos..rule_pos.pos]); util::process_text(&doc, &content.as_str()[cursor.pos..rule_pos.pos]);
if !text_content.is_empty() { if !text_content.is_empty() {
state.push( self.push(
&doc, &doc,
Box::new(Text::new( Box::new(Text::new(
Token::new(cursor.pos..rule_pos.pos, source.clone()), Token::new(cursor.pos..rule_pos.pos, source.clone()),
@ -87,13 +230,12 @@ impl Parser for LangParser {
); );
} }
if let Some((rule_index, match_data)) = result.take() { if let Some(rule) = rule {
// Rule callback // Rule callback
let dd: &'a dyn Document = unsafe { std::mem::transmute(&doc as &dyn Document) }; let dd: &'a dyn Document = unsafe { std::mem::transmute(&doc as &dyn Document) };
let (new_cursor, reports) = let (new_cursor, reports) = rule.on_match(self, dd, rule_pos, match_data);
self.rules[rule_index].on_match(&state, dd, rule_pos, match_data);
self.handle_reports(reports); self.handle_reports(doc.source(), reports);
// Advance // Advance
cursor = new_cursor; cursor = new_cursor;
@ -104,14 +246,14 @@ impl Parser for LangParser {
} }
} }
// Rule States // State
self.handle_reports(state.shared.rule_state.borrow_mut().on_scope_end( self.handle_reports(
&state, doc.source(),
&doc, self.state_mut()
super::state::Scope::DOCUMENT, .on_scope_end(self, &doc, super::state::Scope::DOCUMENT),
)); );
state.push( self.push(
&doc, &doc,
Box::new(DocumentEnd(Token::new( Box::new(DocumentEnd(Token::new(
doc.source().content().len()..doc.source().content().len(), doc.source().content().len()..doc.source().content().len(),
@ -119,26 +261,26 @@ impl Parser for LangParser {
))), ))),
); );
return (Box::new(doc), state); return Box::new(doc);
} }
fn parse_into<'p, 'a, 'doc>( fn parse_into<'a>(&self, source: Rc<dyn Source>, document: &'a dyn Document<'a>) {
&'p self, let mut matches = Vec::new();
state: ParserState<'p, 'a>, for _ in 0..self.rules.len() {
source: Rc<dyn Source>, matches.push((0usize, None));
document: &'doc dyn Document<'doc>, }
) -> ParserState<'p, 'a> {
let content = source.content(); let content = source.content();
let mut cursor = Cursor::new(0usize, source.clone()); let mut cursor = Cursor::new(0usize, source.clone());
loop { loop {
let (rule_pos, mut result) = state.update_matches(&cursor); let (rule_pos, rule, match_data) = self.update_matches(&cursor, &mut matches);
// Unmatched content // Unmatched content
let text_content = let text_content =
util::process_text(document, &content.as_str()[cursor.pos..rule_pos.pos]); util::process_text(document, &content.as_str()[cursor.pos..rule_pos.pos]);
if !text_content.is_empty() { if !text_content.is_empty() {
state.push( self.push(
document, document,
Box::new(Text::new( Box::new(Text::new(
Token::new(cursor.pos..rule_pos.pos, source.clone()), Token::new(cursor.pos..rule_pos.pos, source.clone()),
@ -147,12 +289,11 @@ impl Parser for LangParser {
); );
} }
if let Some((rule_index, match_data)) = result.take() { if let Some(rule) = rule {
// Rule callback // Rule callback
let (new_cursor, reports) = let (new_cursor, reports) = (*rule).on_match(self, document, rule_pos, match_data);
self.rules[rule_index].on_match(&state, document, rule_pos, match_data);
self.handle_reports(reports); self.handle_reports(document.source(), reports);
// Advance // Advance
cursor = new_cursor; cursor = new_cursor;
@ -163,7 +304,6 @@ impl Parser for LangParser {
} }
} }
return state;
// State // State
//self.handle_reports(source.clone(), //self.handle_reports(source.clone(),
// self.state_mut().on_scope_end(&self, &document, super::state::Scope::DOCUMENT)); // self.state_mut().on_scope_end(&self, &document, super::state::Scope::DOCUMENT));
@ -171,3 +311,43 @@ impl Parser for LangParser {
//return doc; //return doc;
} }
} }
impl KernelHolder for LangParser {
fn get_kernel(&self, name: &str) -> Option<RefMut<'_, Kernel>> {
RefMut::filter_map(self.kernels.borrow_mut(), |map| map.get_mut(name)).ok()
}
fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel> {
//TODO do not get
self.kernels.borrow_mut().insert(name.clone(), kernel);
self.get_kernel(name.as_str()).unwrap()
}
}
impl StyleHolder for LangParser {
fn element_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> {
self.styles.borrow()
}
fn element_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
self.styles.borrow_mut()
}
}
impl LayoutHolder for LangParser {
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>> { self.layouts.borrow() }
fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>> {
self.layouts.borrow_mut()
}
}
impl CustomStyleHolder for LangParser {
fn custom_styles(&self) -> Ref<'_, HashMap<String, Rc<dyn CustomStyle>>> {
self.custom_styles.borrow()
}
fn custom_styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn CustomStyle>>> {
self.custom_styles.borrow_mut()
}
}

View file

@ -4,6 +4,3 @@ pub mod rule;
pub mod source; pub mod source;
pub mod state; pub mod state;
pub mod util; pub mod util;
pub mod style;
pub mod layout;
pub mod customstyle;

View file

@ -1,30 +1,20 @@
use ariadne::Label;
use ariadne::Report;
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::Ref;
use std::collections::HashSet; use std::cell::RefMut;
use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use super::customstyle::CustomStyleHolder;
use super::layout::LayoutHolder;
use super::rule::Rule; use super::rule::Rule;
use super::source::Cursor; use super::source::Cursor;
use super::source::Source; use super::source::Source;
use super::state::RuleStateHolder; use super::state::StateHolder;
use super::style::StyleHolder; use crate::document::customstyle::CustomStyleHolder;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::document::DocumentAccessors;
use crate::document::element::ContainerElement;
use crate::document::element::ElemKind;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::layout::LayoutHolder;
use crate::document::style::StyleHolder;
use crate::elements::customstyle::CustomStyleRule; use crate::elements::customstyle::CustomStyleRule;
use crate::elements::paragraph::Paragraph;
use crate::lua::kernel::Kernel;
use crate::lua::kernel::KernelHolder; use crate::lua::kernel::KernelHolder;
use crate::parser::source::SourceFile;
use crate::parser::source::VirtualSource;
use ariadne::Color; use ariadne::Color;
#[derive(Debug)] #[derive(Debug)]
@ -55,137 +45,94 @@ impl ReportColors {
} }
} }
/// The state that is shared with the state's childre pub trait Parser: KernelHolder + StyleHolder + LayoutHolder + CustomStyleHolder {
pub struct SharedState { /// Gets the colors for formatting errors
pub rule_state: RefCell<RuleStateHolder>,
/// The lua [`Kernel`]s
pub kernels: RefCell<KernelHolder>,
/// The styles
pub styles: RefCell<StyleHolder>,
/// The layouts
pub layouts: RefCell<LayoutHolder>,
/// The custom styles
pub custom_styles: RefCell<CustomStyleHolder>,
}
impl SharedState {
/// Construct a new empty shared state
pub(self) fn new(parser: &dyn Parser) -> Self {
let s = Self {
rule_state: RefCell::new(RuleStateHolder::default()),
kernels: RefCell::new(KernelHolder::default()),
styles: RefCell::new(StyleHolder::default()),
layouts: RefCell::new(LayoutHolder::default()),
custom_styles: RefCell::new(CustomStyleHolder::default()),
};
// Register default kernel
s.kernels
.borrow_mut()
.insert("main".to_string(), Kernel::new(parser));
// Default styles & layouts
parser.rules().iter().for_each(|rule| {
rule.register_styles(&mut *s.styles.borrow_mut());
rule.register_layouts(&mut *s.layouts.borrow_mut());
});
s
}
}
/// The state of the parser
pub struct ParserState<'a, 'b> {
/// The parser for which this state exists
pub parser: &'a dyn Parser,
/// The (optional) parent state
parent: Option<&'b ParserState<'a, 'b>>,
/// The position of the matches in the current state
matches: RefCell<Vec<(usize, Option<Box<dyn Any>>)>>,
/// State shared among all states
pub shared: Rc<SharedState>,
}
/// Represents the state of the parser
///
/// This state has some shared data from [`SharedState`] which gets shared
/// with the children of that state, see [`ParserState::with_state`]
impl<'a, 'b> ParserState<'a, 'b> {
/// Constructs a new state for a given parser with an optional parent
/// ///
/// Parent should be None when parsing a brand new document. If you have to /// When colors are disabled, all colors should resolve to empty string
/// set the parent to Some(..) (e.g for imports or sub-document), be sure fn colors(&self) -> &ReportColors;
/// to use the [`ParserState::with_state`] method instead, this create a
/// RAII lived state for use within bounded lifetime.
pub fn new(parser: &'a dyn Parser, parent: Option<&'a ParserState<'a, 'b>>) -> Self {
let matches = parser.rules().iter().map(|_| (0, None)).collect::<Vec<_>>();
let shared = if let Some(parent) = &parent {
parent.shared.clone()
} else {
Rc::new(SharedState::new(parser))
};
Self { /// Gets a reference to all the [`Rule`]s defined for the parser
parser, fn rules(&self) -> &Vec<Box<dyn Rule>>;
parent, /// Gets a mutable reference to all the [`Rule`]s defined for the parser
matches: RefCell::new(matches), fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
shared,
fn state(&self) -> Ref<'_, StateHolder>;
fn state_mut(&self) -> RefMut<'_, StateHolder>;
fn has_error(&self) -> bool;
/// Add an [`Element`] to the [`Document`]
fn push<'a>(&self, doc: &dyn Document, elem: Box<dyn Element>);
/// Parse [`Source`] into a new [`Document`]
fn parse<'a>(
&self,
source: Rc<dyn Source>,
parent: Option<&'a dyn Document<'a>>,
) -> Box<dyn Document<'a> + 'a>;
/// Parse [`Source`] into an already existing [`Document`]
fn parse_into<'a>(&self, source: Rc<dyn Source>, document: &'a dyn Document<'a>);
}
pub trait ParserStrategy {
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String>;
fn update_matches(
&self,
cursor: &Cursor,
matches: &mut Vec<(usize, Option<Box<dyn Any>>)>,
) -> (Cursor, Option<&Box<dyn Rule>>, Option<Box<dyn Any>>);
}
impl<T: Parser> ParserStrategy for T {
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> {
let rule_name = (*rule).name();
// Error on duplicate rule
if let Some(_) = self.rules().iter().find(|rule| rule.name() == rule_name)
{
return Err(format!(
"Attempted to introduce duplicate rule: `{rule_name}`"
));
} }
}
/// Runs a procedure with a new state that inherits the [`SharedState`] state from [`self`] match after {
/// Some(name) => {
/// Note: When parsing a new document, create a new state, then the parsing process let before = self
/// creates states using this method .rules()
pub fn with_state<F, R>(&self, f: F) -> R .iter()
where .enumerate()
F: FnOnce(ParserState) -> R, .find(|(_pos, r)| (r).name() == name);
{
let new_state = ParserState::new(self.parser, Some(self));
f(new_state)
}
/// Updates matches from a given start position e.g [`Cursor`] match before {
/// Some((pos, _)) => self.rules_mut().insert(pos + 1, rule),
/// # Return _ => {
/// return Err(format!(
/// 1. The cursor position after updating the matches "Unable to find rule named `{name}`, to insert rule `{}` after it",
/// 2. (Optional) The winning match with it's match data rule.name()
/// If the winning match is None, it means that the document has no more ))
/// rule to match. I.e The rest of the content should be added as a }
/// [`Text`] element. }
/// The match data should be passed to the [`Rule::on_match`] method. }
/// _ => self.rules_mut().push(rule),
/// # Strategy }
///
/// This function call [`Rule::next_match`] on the rules defined for the
/// parser. It then takes the rule that has the closest `next_match` and
/// returns it. If next_match starts on an escaped character i.e `\\`,
/// then it starts over to find another match for that rule.
/// In case multiple rules have the same `next_match`, the rules that are
/// defined first in the parser are prioritized. See [Parser::add_rule] for
/// information on how to prioritize rules.
///
/// Notes that the result of every call to [`Rule::next_match`] gets stored
/// in a table: [`ParserState::matches`]. Until the cursor steps over a
/// position in the table, `next_match` won't be called.
pub fn update_matches(&self, cursor: &Cursor) -> (Cursor, Option<(usize, Box<dyn Any>)>) {
let mut matches_borrow = self.matches.borrow_mut();
self.parser Ok(())
.rules() }
fn update_matches(
&self,
cursor: &Cursor,
matches: &mut Vec<(usize, Option<Box<dyn Any>>)>,
) -> (Cursor, Option<&Box<dyn Rule>>, Option<Box<dyn Any>>) {
// Update matches
// TODO: Trivially parellalizable
self.rules()
.iter() .iter()
.zip(matches_borrow.iter_mut()) .zip(matches.iter_mut())
.for_each(|(rule, (matched_at, match_data))| { .for_each(|(rule, (matched_at, match_data))| {
// Don't upate if not stepped over yet // Don't upate if not stepped over yet
if *matched_at > cursor.pos { if *matched_at > cursor.pos && rule.downcast_ref::<CustomStyleRule>().is_none() {
// TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule // TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule
return; return;
} }
@ -223,237 +170,23 @@ impl<'a, 'b> ParserState<'a, 'b> {
}); });
// Get winning match // Get winning match
let (winner, next_pos) = matches_borrow let (winner, (next_pos, _match_data)) = matches
.iter() .iter()
.enumerate() .enumerate()
.min_by_key(|(_, (pos, _))| pos) .min_by_key(|(_, (pos, _match_data))| pos)
.map(|(winner, (pos, _))| (winner, *pos))
.unwrap(); .unwrap();
if *next_pos == usize::MAX
if next_pos == usize::MAX
// No rule has matched // No rule has matched
{ {
let content = cursor.source.content(); let content = cursor.source.content();
// No winners, i.e no matches left // No winners, i.e no matches left
return (cursor.at(content.len()), None); return (cursor.at(content.len()), None, None);
} }
return ( (
cursor.at(next_pos), cursor.at(*next_pos),
Some((winner, matches_borrow[winner].1.take().unwrap())), Some(&self.rules()[winner]),
); std::mem::replace(&mut matches[winner].1, None),
} )
}
/// Add an [`Element`] to the [`Document`]
pub fn push(&self, doc: &dyn Document, elem: Box<dyn Element>) {
if elem.kind() == ElemKind::Inline || elem.kind() == ElemKind::Invisible {
let mut paragraph = doc
.last_element_mut::<Paragraph>()
.or_else(|| {
doc.push(Box::new(Paragraph {
location: elem.location().clone(),
content: Vec::new(),
}));
doc.last_element_mut::<Paragraph>()
})
.unwrap();
paragraph.push(elem).unwrap();
} else {
// Process paragraph events
if doc.last_element::<Paragraph>().is_some_and(|_| true) {
self.parser
.handle_reports(self.shared.rule_state.borrow_mut().on_scope_end(
&self,
doc,
super::state::Scope::PARAGRAPH,
));
}
doc.push(elem);
}
}
/// Resets the position and the match_data for a given rule. This is used
/// in order to have 'dynamic' rules that may not match at first, but their
/// matching rule is modified through the parsing process.
///
/// This function also recursively calls itself on it's `parent`, in order
/// to fully reset the match.
///
/// See [`CustomStyleRule`] for an example of how this is used.
///
/// # Error
///
/// Returns an error if `rule_name` was not found in the parser's ruleset.
pub fn reset_match(&self, rule_name: &str) -> Result<(), String>
{
if self.parser.rules().iter()
.zip(self.matches.borrow_mut().iter_mut())
.try_for_each(|(rule, (match_pos, match_data))| {
if rule.name() != rule_name { return Ok(()) }
*match_pos = 0;
match_data.take();
Err(())
}).is_ok()
{
return Err(format!("Could not find rule: {rule_name}"));
}
// Resurcively reset
if let Some(parent) = self.parent
{
return parent.reset_match(rule_name);
}
Ok(())
}
}
pub trait Parser {
/// Gets the colors for formatting errors
///
/// When colors are disabled, all colors should resolve to empty string
fn colors(&self) -> &ReportColors;
/// Gets a reference to all the [`Rule`]s defined for the parser
fn rules(&self) -> &Vec<Box<dyn Rule>>;
/// Gets a mutable reference to all the [`Rule`]s defined for the parser
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
/// Whether the parser emitted an error during it's parsing process
fn has_error(&self) -> bool;
/// Parse [`Source`] into a new [`Document`]
///
/// # Errors
///
/// This method will not fail because we try to optimistically recover from
/// parsing errors. However the resulting document should not get compiled
/// if an error has happenedn, see [`Parser::has_error()`] for reference
///
/// # Returns
///
/// This method returns the resulting [`Document`] after psrsing `source`,
/// note that the [`ParserState`] is only meant to perform testing and not
/// meant to be reused.
fn parse<'p, 'a, 'doc>(
&'p self,
state: ParserState<'p, 'a>,
source: Rc<dyn Source>,
parent: Option<&'doc dyn Document<'doc>>,
) -> (Box<dyn Document<'doc> + 'doc>, ParserState<'p, 'a>);
/// Parse [`Source`] into an already existing [`Document`]
///
/// # Errors
///
/// This method will not fail because we try to optimistically recover from
/// parsing errors. However the resulting document should not get compiled
/// if an error has happened see [`Parser::has_error()`] for reference
///
/// # Returns
///
/// The returned [`ParserState`] is not meant to be reused, it's meant for
/// testing.
fn parse_into<'p, 'a, 'doc>(
&'p self,
state: ParserState<'p, 'a>,
source: Rc<dyn Source>,
document: &'doc dyn Document<'doc>,
) -> ParserState<'p, 'a>;
/// Adds a rule to the parser.
///
/// # Warning
///
/// This method must not be called if a [`ParserState`] for this parser exists.
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> {
if let Some(_) = self
.rules()
.iter()
.find(|other_rule| other_rule.name() == rule.name())
{
return Err(format!(
"Attempted to introduce duplicate rule: `{}`",
rule.name()
));
}
// Try to insert after
if let Some(after) = after {
let index = self
.rules()
.iter()
.enumerate()
.find(|(_, rule)| rule.name() == after)
.map(|(idx, _)| idx);
if let Some(index) = index {
self.rules_mut().insert(index, rule);
} else {
return Err(format!("Unable to find rule `{after}` to insert after"));
}
} else {
self.rules_mut().push(rule);
}
Ok(())
}
/// Handles the reports produced by parsing. The default is to output them
/// to stderr, but you are free to modify it.
fn handle_reports(&self, reports: Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
for mut report in reports {
let mut sources: HashSet<Rc<dyn Source>> = HashSet::new();
fn recurse_source(sources: &mut HashSet<Rc<dyn Source>>, source: Rc<dyn Source>) {
sources.insert(source.clone());
match source.location() {
Some(parent) => {
let parent_source = parent.source();
if sources.get(&parent_source).is_none() {
recurse_source(sources, parent_source);
}
}
None => {}
}
}
report.labels.iter().for_each(|label| {
recurse_source(&mut sources, label.span.0.clone());
});
let cache = sources
.iter()
.map(|source| (source.clone(), source.content().clone()))
.collect::<Vec<(Rc<dyn Source>, String)>>();
cache.iter().for_each(|(source, _)| {
if let Some(location) = source.location() {
if let Some(_s) = source.downcast_ref::<SourceFile>() {
report.labels.push(
Label::new((location.source(), location.start() + 1..location.end()))
.with_message("In file included from here")
.with_order(-1),
);
};
if let Some(_s) = source.downcast_ref::<VirtualSource>() {
let start = location.start()
+ (location.source().content().as_bytes()[location.start()]
== '\n' as u8)
.then_some(1)
.unwrap_or(0);
report.labels.push(
Label::new((location.source(), start..location.end()))
.with_message("In evaluation of")
.with_order(-1),
);
};
}
});
report.eprint(ariadne::sources(cache)).unwrap()
}
}
} }

View file

@ -1,9 +1,7 @@
use super::layout::LayoutHolder; use super::parser::Parser;
use super::parser::ParserState;
use super::source::Cursor; use super::source::Cursor;
use super::source::Source; use super::source::Source;
use super::source::Token; use super::source::Token;
use super::style::StyleHolder;
use crate::document::document::Document; use crate::document::document::Document;
use ariadne::Report; use ariadne::Report;
use downcast_rs::impl_downcast; use downcast_rs::impl_downcast;
@ -19,24 +17,23 @@ pub trait Rule: Downcast {
/// Returns rule's name /// Returns rule's name
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
/// Finds the next match starting from [`cursor`] /// Finds the next match starting from [`cursor`]
fn next_match(&self, state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)>; fn next_match(&self, parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)>;
/// Callback when rule matches /// Callback when rule matches
fn on_match<'a>( fn on_match<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
match_data: Box<dyn Any>, match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>); ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>);
/// Export bindings to lua
/// Registers lua bindings fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
/// Registers default styles /// Registers default styles
fn register_styles(&self, _holder: &mut StyleHolder) {} fn register_styles(&self, _parser: &dyn Parser) {}
/// Registers default layouts /// Registers default layouts
fn register_layouts(&self, _holder: &mut LayoutHolder) {} fn register_layouts(&self, _parser: &dyn Parser) {}
} }
impl_downcast!(Rule); impl_downcast!(Rule);
@ -56,22 +53,24 @@ pub trait RegexRule {
fn on_regex_match<'a>( fn on_regex_match<'a>(
&self, &self,
index: usize, index: usize,
state: &ParserState, parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
token: Token, token: Token,
matches: regex::Captures, matches: regex::Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>; ) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>;
fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] } fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
fn register_styles(&self, _holder: &mut StyleHolder) {} fn register_styles(&self, _parser: &dyn Parser) {}
fn register_layouts(&self, _holder: &mut LayoutHolder) {} fn register_layouts(&self, _parser: &dyn Parser) {}
} }
impl<T: RegexRule + 'static> Rule for T { impl<T: RegexRule + 'static> Rule for T {
fn name(&self) -> &'static str { RegexRule::name(self) } fn name(&self) -> &'static str {
RegexRule::name(self)
}
/// Finds the next match starting from [`cursor`] /// Finds the next match starting from [`cursor`]
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
let content = cursor.source.content(); let content = cursor.source.content();
let mut found: Option<(usize, usize)> = None; let mut found: Option<(usize, usize)> = None;
self.regexes().iter().enumerate().for_each(|(id, re)| { self.regexes().iter().enumerate().for_each(|(id, re)| {
@ -93,13 +92,18 @@ impl<T: RegexRule + 'static> Rule for T {
fn on_match<'a>( fn on_match<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &'a (dyn Document<'a> + 'a), document: &'a (dyn Document<'a> + 'a),
cursor: Cursor, cursor: Cursor,
match_data: Box<dyn Any>, match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let content = cursor.source.content(); let content = cursor.source.content();
let index = match_data.downcast::<usize>().unwrap(); let index = unsafe {
match_data
.unwrap_unchecked()
.downcast::<usize>()
.unwrap_unchecked()
};
let re = &self.regexes()[*index]; let re = &self.regexes()[*index];
let captures = re.captures_at(content.as_str(), cursor.pos).unwrap(); let captures = re.captures_at(content.as_str(), cursor.pos).unwrap();
@ -108,15 +112,19 @@ impl<T: RegexRule + 'static> Rule for T {
let token_end = token.end(); let token_end = token.end();
return ( return (
cursor.at(token_end), cursor.at(token_end),
self.on_regex_match(*index, state, document, token, captures), self.on_regex_match(*index, parser, document, token, captures),
); );
} }
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
self.register_bindings(lua) self.lua_bindings(lua)
} }
fn register_styles(&self, holder: &mut StyleHolder) { self.register_styles(holder); } fn register_styles(&self, parser: &dyn Parser) {
self.register_styles(parser);
}
fn register_layouts(&self, holder: &mut LayoutHolder) { self.register_layouts(holder); } fn register_layouts(&self, parser: &dyn Parser) {
self.register_layouts(parser);
}
} }

View file

@ -9,7 +9,7 @@ use downcast_rs::Downcast;
use crate::document::document::Document; use crate::document::document::Document;
use super::parser::ParserState; use super::parser::Parser;
use super::source::Source; use super::source::Source;
/// Scope for state objects /// Scope for state objects
@ -25,69 +25,75 @@ pub enum Scope {
PARAGRAPH = 2, PARAGRAPH = 2,
} }
pub trait RuleState: Downcast { pub trait State: Downcast {
/// Returns the state's [`Scope`] /// Returns the state's [`Scope`]
fn scope(&self) -> Scope; fn scope(&self) -> Scope;
/// Callback called when state goes out of scope /// Callback called when state goes out of scope
fn on_remove<'a>( fn on_remove<'a>(
&self, &self,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>>; ) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>>;
} }
impl_downcast!(RuleState); impl_downcast!(State);
impl core::fmt::Debug for dyn RuleState { impl core::fmt::Debug for dyn State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "State{{Scope: {:#?}}}", self.scope()) write!(f, "State{{Scope: {:#?}}}", self.scope())
} }
} }
/// Object owning all the states /// Object owning all the states
#[derive(Default)] #[derive(Debug)]
pub struct RuleStateHolder { pub struct StateHolder {
states: HashMap<String, Rc<RefCell<dyn RuleState>>>, data: HashMap<String, Rc<RefCell<dyn State>>>,
} }
impl RuleStateHolder { impl StateHolder {
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
// Attempts to push [`state`]. On collision, returns an error with the already present state
pub fn insert( pub fn insert(
&mut self, &mut self,
name: String, name: String,
state: Rc<RefCell<dyn RuleState>>, state: Rc<RefCell<dyn State>>,
) -> Result<Rc<RefCell<dyn RuleState>>, String> { ) -> Result<Rc<RefCell<dyn State>>, Rc<RefCell<dyn State>>> {
if self.states.contains_key(name.as_str()) { match self.data.insert(name, state.clone()) {
return Err(format!("Attempted to insert duplicate RuleState: {name}")); Some(state) => Err(state),
_ => Ok(state),
} }
self.states.insert(name, state.clone());
Ok(state)
} }
pub fn get(&self, state_name: &str) -> Option<Rc<RefCell<dyn RuleState>>> { pub fn query(&self, name: &String) -> Option<Rc<RefCell<dyn State>>> {
self.states.get(state_name).map(|state| state.clone()) self.data.get(name).map_or(None, |st| Some(st.clone()))
} }
pub fn on_scope_end( pub fn on_scope_end(
&mut self, &mut self,
state: &ParserState, parser: &dyn Parser,
document: &dyn Document, document: &dyn Document,
scope: Scope, scope: Scope,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![]; let mut result = vec![];
self.states.retain(|_name, rule_state| { self.data.retain(|_name, state| {
if rule_state.borrow().scope() >= scope { if state.borrow().scope() >= scope {
rule_state state
.borrow_mut() .borrow()
.on_remove(state, document) .on_remove(parser, document)
.drain(..) .drain(..)
.for_each(|report| reports.push(report)); .for_each(|report| result.push(report));
false false
} else { } else {
true true
} }
}); });
return reports; return result;
} }
} }

View file

@ -8,7 +8,7 @@ use crate::document::document::DocumentAccessors;
use crate::document::element::ElemKind; use crate::document::element::ElemKind;
use crate::elements::paragraph::Paragraph; use crate::elements::paragraph::Paragraph;
use super::parser::ParserState; use super::parser::Parser;
use super::source::Source; use super::source::Source;
/// Processes text for escape characters and paragraphing /// Processes text for escape characters and paragraphing
@ -136,24 +136,18 @@ pub fn process_escaped<S: AsRef<str>>(escape: char, token: &'static str, content
/// Parses source into a single paragraph /// Parses source into a single paragraph
/// If source contains anything but a single paragraph, an error is returned /// If source contains anything but a single paragraph, an error is returned
pub fn parse_paragraph<'a>( pub fn parse_paragraph<'a>(
state: &ParserState, parser: &dyn Parser,
source: Rc<dyn Source>, source: Rc<dyn Source>,
document: &'a dyn Document<'a>, document: &'a dyn Document<'a>,
) -> Result<Box<Paragraph>, &'static str> { ) -> Result<Box<Paragraph>, &'static str> {
let parsed = state.with_state(|new_state| -> Box<dyn Document> { let parsed = parser.parse(source.clone(), Some(document));
new_state
.parser
.parse(new_state, source.clone(), Some(document))
.0
});
if parsed.content().borrow().len() > 1 { if parsed.content().borrow().len() > 1 {
return Err("Parsed document contains more than a single paragraph"); return Err("Parsed document contains more than a single paragraph");
} else if parsed.content().borrow().len() == 0 { } else if parsed.content().borrow().len() == 0 {
return Err("Parsed document is empty"); return Err("Parsed document is empty");
} else if parsed.last_element::<Paragraph>().is_none() { } else if parsed.last_element::<Paragraph>().is_none() {
return Err("Parsed element is not a paragraph"); return Err("Parsed element is not a paragraph");
} else if state.parser.has_error() { } else if parser.has_error() {
// FIXME: If parser had an error before, this wold trigger
return Err("Parser error"); return Err("Parser error");
} }
@ -423,23 +417,20 @@ mod tests {
(&doc as &dyn Document) (&doc as &dyn Document)
.last_element_mut::<Paragraph>() .last_element_mut::<Paragraph>()
.unwrap() .unwrap()
.push(Box::new(Comment::new(tok.clone(), "COMMENT".to_string()))) .push(Box::new(Comment::new(tok.clone(), "COMMENT".to_string())));
.unwrap();
assert_eq!(process_text(&doc, "\na"), "a"); assert_eq!(process_text(&doc, "\na"), "a");
// A space is appended as previous element is inline // A space is appended as previous element is inline
(&doc as &dyn Document) (&doc as &dyn Document)
.last_element_mut::<Paragraph>() .last_element_mut::<Paragraph>()
.unwrap() .unwrap()
.push(Box::new(Text::new(tok.clone(), "TEXT".to_string()))) .push(Box::new(Text::new(tok.clone(), "TEXT".to_string())));
.unwrap();
assert_eq!(process_text(&doc, "\na"), " a"); assert_eq!(process_text(&doc, "\na"), " a");
(&doc as &dyn Document) (&doc as &dyn Document)
.last_element_mut::<Paragraph>() .last_element_mut::<Paragraph>()
.unwrap() .unwrap()
.push(Box::new(Style::new(tok.clone(), 0, false))) .push(Box::new(Style::new(tok.clone(), 0, false)));
.unwrap();
assert_eq!(process_text(&doc, "\na"), " a"); assert_eq!(process_text(&doc, "\na"), " a");
} }