Compare commits
3 commits
cf3491e5a7
...
62e0aeecef
Author | SHA1 | Date | |
---|---|---|---|
62e0aeecef | |||
ce9effd465 | |||
84e4c17fda |
36 changed files with 1233 additions and 1123 deletions
|
@ -1,57 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,23 +1,24 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::parser::source::Source;
|
||||
|
||||
use super::{document::{Document, Scope}, element::Element};
|
||||
use super::document::Document;
|
||||
use super::document::Scope;
|
||||
use super::element::Element;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LangDocument<'a> {
|
||||
source: Rc<dyn Source>,
|
||||
parent: Option<&'a dyn Document<'a>>, /// Document's parent
|
||||
|
||||
parent: Option<&'a dyn Document<'a>>,
|
||||
/// Document's parent
|
||||
// FIXME: Render these fields private
|
||||
pub content: RefCell<Vec<Box<dyn Element>>>,
|
||||
pub scope: RefCell<Scope>,
|
||||
}
|
||||
|
||||
impl<'a> LangDocument<'a>
|
||||
{
|
||||
pub fn new(source: Rc<dyn Source>, parent: Option<&'a dyn Document<'a>>) -> Self
|
||||
{
|
||||
impl<'a> LangDocument<'a> {
|
||||
pub fn new(source: Rc<dyn Source>, parent: Option<&'a dyn Document<'a>>) -> Self {
|
||||
Self {
|
||||
source: source,
|
||||
parent: parent,
|
||||
|
@ -30,7 +31,9 @@ impl<'a> LangDocument<'a>
|
|||
impl<'a> Document<'a> for LangDocument<'a> {
|
||||
fn source(&self) -> Rc<dyn Source> { self.source.clone() }
|
||||
|
||||
fn parent(&self) -> Option<&'a dyn Document<'a>> { self.parent.and_then(|p| Some(p as &dyn Document<'a>)) }
|
||||
fn parent(&self) -> Option<&'a dyn Document<'a>> {
|
||||
self.parent.and_then(|p| Some(p as &dyn Document<'a>))
|
||||
}
|
||||
|
||||
fn content(&self) -> &RefCell<Vec<Box<dyn Element>>> { &self.content }
|
||||
|
||||
|
|
|
@ -3,6 +3,3 @@ pub mod references;
|
|||
pub mod langdocument;
|
||||
pub mod element;
|
||||
pub mod variable;
|
||||
pub mod style;
|
||||
pub mod layout;
|
||||
pub mod customstyle;
|
||||
|
|
|
@ -45,6 +45,7 @@ pub mod tests {
|
|||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::ParserState;
|
||||
|
||||
#[test]
|
||||
fn validate_refname_tests() {
|
||||
|
@ -54,7 +55,7 @@ pub mod tests {
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
assert_eq!(validate_refname(&*doc, " abc ", true), Ok("abc"));
|
||||
assert_eq!(
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
use std::{path::PathBuf, rc::Rc};
|
||||
use crate::{elements::text::Text, parser::{parser::Parser, source::{Source, Token, VirtualSource}}};
|
||||
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()
|
||||
pub trait Variable
|
||||
{
|
||||
pub trait Variable {
|
||||
fn location(&self) -> &Token;
|
||||
|
||||
fn name(&self) -> &str;
|
||||
|
@ -15,19 +18,17 @@ pub trait Variable
|
|||
/// Converts variable to a string
|
||||
fn to_string(&self) -> String;
|
||||
|
||||
fn parse<'a>(&self, location: Token, parser: &dyn Parser, document: &'a dyn Document<'a>);
|
||||
fn parse<'a>(&self, state: &ParserState, location: Token, 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 {
|
||||
write!(f, "{}{{{}}}", self.name(), self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BaseVariable
|
||||
{
|
||||
pub struct BaseVariable {
|
||||
location: Token,
|
||||
name: String,
|
||||
value: String,
|
||||
|
@ -35,12 +36,15 @@ pub struct BaseVariable
|
|||
|
||||
impl BaseVariable {
|
||||
pub fn new(location: Token, name: String, value: String) -> Self {
|
||||
Self { location, name, value }
|
||||
Self {
|
||||
location,
|
||||
name,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Variable for BaseVariable
|
||||
{
|
||||
impl Variable for BaseVariable {
|
||||
fn location(&self) -> &Token { &self.location }
|
||||
|
||||
fn name(&self) -> &str { self.name.as_str() }
|
||||
|
@ -52,33 +56,37 @@ impl Variable for BaseVariable
|
|||
|
||||
fn to_string(&self) -> String { self.value.clone() }
|
||||
|
||||
fn parse<'a>(&self, _location: Token, parser: &dyn Parser, document: &'a dyn Document<'a>) {
|
||||
fn parse<'a>(&self, state: &ParserState, _location: Token, document: &'a dyn Document<'a>) {
|
||||
let source = Rc::new(VirtualSource::new(
|
||||
self.location().clone(),
|
||||
self.name().to_string(),
|
||||
self.to_string()));
|
||||
self.to_string(),
|
||||
));
|
||||
|
||||
parser.parse_into(source, document);
|
||||
state.with_state(|new_state| {
|
||||
let _ = new_state.parser.parse_into(new_state, source, document);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PathVariable
|
||||
{
|
||||
pub struct PathVariable {
|
||||
location: Token,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl PathVariable
|
||||
{
|
||||
impl PathVariable {
|
||||
pub fn new(location: Token, name: String, path: PathBuf) -> Self {
|
||||
Self { location, name, path }
|
||||
Self {
|
||||
location,
|
||||
name,
|
||||
path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Variable for PathVariable
|
||||
{
|
||||
impl Variable for PathVariable {
|
||||
fn location(&self) -> &Token { &self.location }
|
||||
|
||||
fn name(&self) -> &str { self.name.as_str() }
|
||||
|
@ -90,15 +98,19 @@ impl Variable for PathVariable
|
|||
|
||||
fn to_string(&self) -> String { self.path.to_str().unwrap().to_string() }
|
||||
|
||||
fn parse<'a>(&self, location: Token, parser: &dyn Parser, document: &'a dyn Document) {
|
||||
fn parse<'a>(&self, state: &ParserState, location: Token, document: &'a dyn Document) {
|
||||
let source = Rc::new(VirtualSource::new(
|
||||
location,
|
||||
self.name().to_string(),
|
||||
self.to_string()));
|
||||
self.to_string(),
|
||||
));
|
||||
|
||||
parser.push(document, Box::new(Text::new(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Text::new(
|
||||
Token::new(0..source.content().len(), source),
|
||||
self.to_string()
|
||||
)));
|
||||
self.to_string(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::document::document::Document;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
|
@ -323,7 +323,7 @@ impl CodeRule {
|
|||
)
|
||||
.unwrap(),
|
||||
],
|
||||
properties: PropertyParser{ properties: props },
|
||||
properties: PropertyParser { properties: props },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ impl RegexRule for CodeRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -353,7 +353,7 @@ impl RegexRule for CodeRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!("Code is missing properties: {e}"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -371,7 +371,7 @@ impl RegexRule for CodeRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), props.range()))
|
||||
.with_message(e)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -393,7 +393,7 @@ impl RegexRule for CodeRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), lang.range()))
|
||||
.with_message("No language specified")
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -411,9 +411,9 @@ impl RegexRule for CodeRule {
|
|||
Label::new((token.source().clone(), lang.range()))
|
||||
.with_message(format!(
|
||||
"Language `{}` cannot be found",
|
||||
code_lang.fg(parser.colors().info)
|
||||
code_lang.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -443,7 +443,7 @@ impl RegexRule for CodeRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message("Code content cannot be empty")
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -475,9 +475,9 @@ impl RegexRule for CodeRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), token.start()+1..token.end()))
|
||||
.with_message(format!("Property `line_offset: {}` cannot be converted: {}",
|
||||
prop.fg(parser.colors().info),
|
||||
err.fg(parser.colors().error)))
|
||||
.with_color(parser.colors().warning))
|
||||
prop.fg(state.parser.colors().info),
|
||||
err.fg(state.parser.colors().error)))
|
||||
.with_color(state.parser.colors().warning))
|
||||
.finish());
|
||||
return reports;
|
||||
}
|
||||
|
@ -492,9 +492,9 @@ impl RegexRule for CodeRule {
|
|||
))
|
||||
.with_message(format!(
|
||||
"Property `{}` doesn't exist",
|
||||
err.fg(parser.colors().info)
|
||||
err.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -504,7 +504,7 @@ impl RegexRule for CodeRule {
|
|||
}
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Code::new(
|
||||
token.clone(),
|
||||
|
@ -525,7 +525,7 @@ impl RegexRule for CodeRule {
|
|||
CodeKind::Inline
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Code::new(
|
||||
token.clone(),
|
||||
|
@ -542,7 +542,7 @@ impl RegexRule for CodeRule {
|
|||
reports
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
bindings.push((
|
||||
"push_inline".to_string(),
|
||||
|
@ -554,7 +554,7 @@ impl RegexRule for CodeRule {
|
|||
.get_variable("code.theme")
|
||||
.and_then(|var| Some(var.to_string()));
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Code {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -585,7 +585,7 @@ impl RegexRule for CodeRule {
|
|||
.get_variable("code.theme")
|
||||
.and_then(|var| Some(var.to_string()));
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Code {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -623,7 +623,7 @@ impl RegexRule for CodeRule {
|
|||
.get_variable("code.theme")
|
||||
.and_then(|var| Some(var.to_string()));
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Code {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -644,7 +644,7 @@ impl RegexRule for CodeRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,6 +652,7 @@ impl RegexRule for CodeRule {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
|
||||
#[test]
|
||||
|
@ -679,7 +680,7 @@ fn fact(n: usize) -> usize
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
let borrow = doc.content().borrow();
|
||||
let found = borrow
|
||||
|
@ -725,8 +726,7 @@ fn fact(n: usize) -> usize
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
//let compiler = Compiler::new(Target::HTML, None);
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
let borrow = doc.content().borrow();
|
||||
let found = borrow
|
||||
|
|
|
@ -2,15 +2,13 @@ use crate::compiler::compiler::Compiler;
|
|||
use crate::document::document::Document;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
use std::ops::Range;
|
||||
|
@ -60,7 +58,7 @@ impl RegexRule for CommentRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -78,7 +76,7 @@ impl RegexRule for CommentRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), comment.range()))
|
||||
.with_message("Comment is empty")
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -88,7 +86,13 @@ impl RegexRule for CommentRule {
|
|||
}
|
||||
};
|
||||
|
||||
parser.push(document, Box::new(Comment::new(token.clone(), content)));
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Comment {
|
||||
location: token.clone(),
|
||||
content,
|
||||
}),
|
||||
);
|
||||
|
||||
return reports;
|
||||
}
|
||||
|
@ -97,9 +101,10 @@ impl RegexRule for CommentRule {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::elements::style::Style;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::elements::style::Style;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -118,7 +123,7 @@ COMMENT ::Test
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::lua::kernel::Kernel;
|
||||
use std::any::Any;
|
||||
use std::cell::Ref;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
|
@ -13,21 +15,19 @@ use mlua::Error::BadArgument;
|
|||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
|
||||
use crate::document::customstyle::CustomStyle;
|
||||
use crate::document::customstyle::CustomStyleToken;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::document::DocumentAccessors;
|
||||
use crate::lua::kernel::KernelContext;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::customstyle::CustomStyle;
|
||||
use crate::parser::customstyle::CustomStyleToken;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::Rule;
|
||||
use crate::parser::source::Cursor;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
use crate::parser::state::RuleState;
|
||||
use crate::parser::state::Scope;
|
||||
use crate::parser::state::State;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use super::paragraph::Paragraph;
|
||||
|
||||
|
@ -47,75 +47,78 @@ impl CustomStyle for LuaCustomStyle {
|
|||
fn on_start<'a>(
|
||||
&self,
|
||||
location: Token,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let kernel = parser.get_kernel("main").unwrap();
|
||||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let kernel: Ref<'_, Kernel> =
|
||||
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 {
|
||||
location: location.clone(),
|
||||
parser,
|
||||
state,
|
||||
document,
|
||||
};
|
||||
|
||||
let mut result = Ok(());
|
||||
let mut reports = vec![];
|
||||
kernel.run_with_context(ctx, |lua| {
|
||||
let chunk = lua.load(self.start.as_str());
|
||||
if let Err(err) = chunk.eval::<()>() {
|
||||
result = Err(
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, location.source(), location.start())
|
||||
.with_message("Lua execution failed")
|
||||
.with_label(
|
||||
Label::new((location.source(), location.range.clone()))
|
||||
.with_message(err.to_string())
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_note(format!(
|
||||
"When trying to start custom style {}",
|
||||
self.name().fg(parser.colors().info)
|
||||
self.name().fg(state.parser.colors().info)
|
||||
))
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
result
|
||||
reports
|
||||
}
|
||||
|
||||
fn on_end<'a>(
|
||||
&self,
|
||||
location: Token,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
) -> Result<(), Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let kernel = parser.get_kernel("main").unwrap();
|
||||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let kernel: Ref<'_, Kernel> =
|
||||
Ref::map(state.shared.kernels.borrow(), |b| b.get("main").unwrap());
|
||||
let ctx = KernelContext {
|
||||
location: location.clone(),
|
||||
parser,
|
||||
state,
|
||||
document,
|
||||
};
|
||||
|
||||
let mut result = Ok(());
|
||||
let mut reports = vec![];
|
||||
kernel.run_with_context(ctx, |lua| {
|
||||
let chunk = lua.load(self.end.as_str());
|
||||
if let Err(err) = chunk.eval::<()>() {
|
||||
result = Err(
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, location.source(), location.start())
|
||||
.with_message("Lua execution failed")
|
||||
.with_label(
|
||||
Label::new((location.source(), location.range.clone()))
|
||||
.with_message(err.to_string())
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_note(format!(
|
||||
"When trying to end custom style {}",
|
||||
self.name().fg(parser.colors().info)
|
||||
self.name().fg(state.parser.colors().info)
|
||||
))
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
result
|
||||
reports
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,12 +126,12 @@ struct CustomStyleState {
|
|||
toggled: HashMap<String, Token>,
|
||||
}
|
||||
|
||||
impl State for CustomStyleState {
|
||||
impl RuleState for CustomStyleState {
|
||||
fn scope(&self) -> Scope { Scope::PARAGRAPH }
|
||||
|
||||
fn on_remove<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
@ -154,15 +157,15 @@ impl State for CustomStyleState {
|
|||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Style {} starts here",
|
||||
style.fg(parser.colors().info)
|
||||
style.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new(paragraph_end)
|
||||
.with_order(1)
|
||||
.with_message(format!("Paragraph ends here"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_note("Styles cannot span multiple documents (i.e @import)")
|
||||
.finish(),
|
||||
|
@ -173,22 +176,22 @@ impl State for CustomStyleState {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct CustomStyleRule;
|
||||
static STATE_NAME: &'static str = "elements.custom_style";
|
||||
|
||||
lazy_static! {
|
||||
static ref STATE_NAME: String = "elements.custom_style".to_string();
|
||||
}
|
||||
pub struct CustomStyleRule;
|
||||
|
||||
impl Rule for CustomStyleRule {
|
||||
fn name(&self) -> &'static str { "Custom Style" }
|
||||
|
||||
fn next_match(&self, parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
let content = cursor.source.content();
|
||||
|
||||
let mut closest_match = usize::MAX;
|
||||
let mut matched_style = (None, false);
|
||||
parser
|
||||
.custom_styles()
|
||||
state
|
||||
.shared
|
||||
.custom_styles
|
||||
.borrow()
|
||||
.iter()
|
||||
.for_each(|(_name, style)| match style.tokens() {
|
||||
CustomStyleToken::Toggle(s) => {
|
||||
|
@ -228,67 +231,61 @@ impl Rule for CustomStyleRule {
|
|||
|
||||
fn on_match<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
cursor: Cursor,
|
||||
match_data: Option<Box<dyn Any>>,
|
||||
match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
let (style, end) = match_data
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.downcast_ref::<(Rc<dyn CustomStyle>, bool)>()
|
||||
.unwrap();
|
||||
|
||||
let query = parser.state().query(&STATE_NAME);
|
||||
let state = match query {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
let mut rule_state_borrow = state.shared.rule_state.borrow_mut();
|
||||
let style_state = match rule_state_borrow.get(STATE_NAME) {
|
||||
Some(rule_state) => rule_state,
|
||||
// Insert as a new state
|
||||
match parser.state_mut().insert(
|
||||
STATE_NAME.clone(),
|
||||
None => {
|
||||
match rule_state_borrow.insert(
|
||||
STATE_NAME.into(),
|
||||
Rc::new(RefCell::new(CustomStyleState {
|
||||
toggled: HashMap::new(),
|
||||
})),
|
||||
) {
|
||||
Err(_) => panic!("Unknown error"),
|
||||
Ok(state) => state,
|
||||
Err(err) => panic!("{err}"),
|
||||
Ok(rule_state) => rule_state,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (close, token) = match style.tokens() {
|
||||
CustomStyleToken::Toggle(s) => {
|
||||
let mut borrow = state.borrow_mut();
|
||||
let state = borrow.downcast_mut::<CustomStyleState>().unwrap();
|
||||
let mut borrow = style_state.as_ref().borrow_mut();
|
||||
let style_state = borrow.downcast_mut::<CustomStyleState>().unwrap();
|
||||
|
||||
match state.toggled.get(style.name()) {
|
||||
Some(_) => {
|
||||
if style_state.toggled.get(style.name()).is_some() {
|
||||
// Terminate style
|
||||
let token =
|
||||
Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
|
||||
let token = Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
|
||||
|
||||
state.toggled.remove(style.name());
|
||||
style_state.toggled.remove(style.name());
|
||||
(true, token)
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
// Start style
|
||||
let token =
|
||||
Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
|
||||
let token = Token::new(cursor.pos..cursor.pos + s.len(), cursor.source.clone());
|
||||
|
||||
state.toggled.insert(style.name().into(), token.clone());
|
||||
style_state
|
||||
.toggled
|
||||
.insert(style.name().into(), token.clone());
|
||||
(false, token)
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomStyleToken::Pair(s_begin, s_end) => {
|
||||
let mut borrow = state.borrow_mut();
|
||||
let state = borrow.downcast_mut::<CustomStyleState>().unwrap();
|
||||
|
||||
let mut borrow = style_state.borrow_mut();
|
||||
let style_state = borrow.downcast_mut::<CustomStyleState>().unwrap();
|
||||
if *end {
|
||||
// Terminate style
|
||||
let token =
|
||||
Token::new(cursor.pos..cursor.pos + s_end.len(), cursor.source.clone());
|
||||
if state.toggled.get(style.name()).is_none() {
|
||||
if style_state.toggled.get(style.name()).is_none() {
|
||||
return (
|
||||
cursor.at(cursor.pos + s_end.len()),
|
||||
vec![
|
||||
|
@ -299,16 +296,16 @@ impl Rule for CustomStyleRule {
|
|||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Cannot end style {} here, is it not started anywhere",
|
||||
style.name().fg(parser.colors().info)
|
||||
style.name().fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
state.toggled.remove(style.name());
|
||||
style_state.toggled.remove(style.name());
|
||||
(true, token)
|
||||
} else {
|
||||
// Start style
|
||||
|
@ -316,7 +313,7 @@ impl Rule for CustomStyleRule {
|
|||
cursor.pos..cursor.pos + s_begin.len(),
|
||||
cursor.source.clone(),
|
||||
);
|
||||
if let Some(start_token) = state.toggled.get(style.name()) {
|
||||
if let Some(start_token) = style_state.toggled.get(style.name()) {
|
||||
return (
|
||||
cursor.at(cursor.pos + s_end.len()),
|
||||
vec![Report::build(
|
||||
|
@ -330,47 +327,43 @@ impl Rule for CustomStyleRule {
|
|||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Style cannot {} starts here",
|
||||
style.name().fg(parser.colors().info)
|
||||
style.name().fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new((start_token.source(), start_token.range.clone()))
|
||||
.with_order(2)
|
||||
.with_message(format!(
|
||||
"Style {} starts previously here",
|
||||
style.name().fg(parser.colors().info)
|
||||
style.name().fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish()],
|
||||
);
|
||||
}
|
||||
|
||||
state.toggled.insert(style.name().into(), token.clone());
|
||||
style_state
|
||||
.toggled
|
||||
.insert(style.name().into(), token.clone());
|
||||
(false, token)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(rep) = if close {
|
||||
style.on_end(token.clone(), parser, document)
|
||||
let reports = if close {
|
||||
style.on_end(token.clone(), state, document)
|
||||
} else {
|
||||
style.on_start(token.clone(), parser, document)
|
||||
} {
|
||||
return (
|
||||
cursor.at(token.end()),
|
||||
vec![unsafe {
|
||||
// TODO
|
||||
std::mem::transmute(rep)
|
||||
}],
|
||||
);
|
||||
} else {
|
||||
(cursor.at(token.end()), vec![])
|
||||
}
|
||||
style.on_start(token.clone(), state, document)
|
||||
};
|
||||
|
||||
(cursor.at(token.end()), unsafe {
|
||||
std::mem::transmute(reports)
|
||||
})
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
|
@ -388,7 +381,9 @@ impl Rule for CustomStyleRule {
|
|||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
|
||||
if let Some(_) =
|
||||
ctx.state.shared.custom_styles.borrow().get(name.as_str())
|
||||
{
|
||||
result = Err(BadArgument {
|
||||
to: Some("define_toggled".to_string()),
|
||||
pos: 1,
|
||||
|
@ -399,7 +394,13 @@ impl Rule for CustomStyleRule {
|
|||
});
|
||||
return;
|
||||
}
|
||||
ctx.parser.insert_custom_style(Rc::new(style));
|
||||
ctx.state
|
||||
.shared
|
||||
.custom_styles
|
||||
.borrow_mut()
|
||||
.insert(Rc::new(style));
|
||||
|
||||
ctx.state.reset_match("Custom Style").unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -443,7 +444,7 @@ impl Rule for CustomStyleRule {
|
|||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
|
||||
if let Some(_) = ctx.state.shared.custom_styles.borrow().get(name.as_str()) {
|
||||
result = Err(BadArgument {
|
||||
to: Some("define_paired".to_string()),
|
||||
pos: 1,
|
||||
|
@ -454,7 +455,9 @@ impl Rule for CustomStyleRule {
|
|||
});
|
||||
return;
|
||||
}
|
||||
ctx.parser.insert_custom_style(Rc::new(style));
|
||||
ctx.state.shared.custom_styles.borrow_mut().insert(Rc::new(style));
|
||||
|
||||
ctx.state.reset_match("Custom Style").unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -464,7 +467,7 @@ impl Rule for CustomStyleRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,6 +476,7 @@ mod tests {
|
|||
use crate::elements::raw::Raw;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -505,7 +509,7 @@ pre |styled| post °Hello°.
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
@ -549,7 +553,7 @@ pre [styled] post (Hello).
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::parser::style::ElementStyle;
|
||||
use std::any::Any;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
@ -15,7 +16,7 @@ use regex::Regex;
|
|||
|
||||
use crate::document::document::Document;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::Rule;
|
||||
use crate::parser::source::Cursor;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -58,7 +59,7 @@ impl ElemStyleRule {
|
|||
impl Rule for ElemStyleRule {
|
||||
fn name(&self) -> &'static str { "Element Style" }
|
||||
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
self.start_re
|
||||
.find_at(cursor.source.content(), cursor.pos)
|
||||
.map_or(None, |m| {
|
||||
|
@ -68,10 +69,10 @@ impl Rule for ElemStyleRule {
|
|||
|
||||
fn on_match<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
_document: &'a (dyn Document<'a> + 'a),
|
||||
cursor: Cursor,
|
||||
_match_data: Option<Box<dyn Any>>,
|
||||
_match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
let mut reports = vec![];
|
||||
let matches = self
|
||||
|
@ -80,7 +81,7 @@ impl Rule for ElemStyleRule {
|
|||
.unwrap();
|
||||
let mut cursor = cursor.at(matches.get(0).unwrap().end() - 1);
|
||||
|
||||
let style = if let Some(key) = matches.get(1) {
|
||||
let style: Rc<dyn ElementStyle> = if let Some(key) = matches.get(1) {
|
||||
let trimmed = key.as_str().trim_start().trim_end();
|
||||
|
||||
// Check if empty
|
||||
|
@ -91,7 +92,7 @@ impl Rule for ElemStyleRule {
|
|||
.with_label(
|
||||
Label::new((cursor.source.clone(), key.range()))
|
||||
.with_message(format!("Expected a non-empty style key",))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -99,7 +100,7 @@ impl Rule for ElemStyleRule {
|
|||
}
|
||||
|
||||
// Check if key exists
|
||||
if !parser.is_style_registered(trimmed) {
|
||||
if !state.shared.styles.borrow().is_registered(trimmed) {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, cursor.source.clone(), key.start())
|
||||
.with_message("Unknown Style Key")
|
||||
|
@ -107,9 +108,9 @@ impl Rule for ElemStyleRule {
|
|||
Label::new((cursor.source.clone(), key.range()))
|
||||
.with_message(format!(
|
||||
"Could not find a style with key: {}",
|
||||
trimmed.fg(parser.colors().info)
|
||||
trimmed.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -117,7 +118,7 @@ impl Rule for ElemStyleRule {
|
|||
return (cursor, reports);
|
||||
}
|
||||
|
||||
parser.current_style(trimmed)
|
||||
state.shared.styles.borrow().current(trimmed)
|
||||
} else {
|
||||
panic!("Unknown error")
|
||||
};
|
||||
|
@ -135,7 +136,7 @@ impl Rule for ElemStyleRule {
|
|||
.with_message(format!(
|
||||
"Unable to parse json string after style key",
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -157,10 +158,10 @@ impl Rule for ElemStyleRule {
|
|||
))
|
||||
.with_message(format!(
|
||||
"Failed to serialize `{}` into style with key `{}`: {err}",
|
||||
json.fg(parser.colors().highlight),
|
||||
style.key().fg(parser.colors().info)
|
||||
json.fg(state.parser.colors().highlight),
|
||||
style.key().fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -171,12 +172,12 @@ impl Rule for ElemStyleRule {
|
|||
}
|
||||
};
|
||||
|
||||
parser.set_current_style(new_style);
|
||||
state.shared.styles.borrow_mut().set_current(new_style);
|
||||
|
||||
(cursor, reports)
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
|
@ -185,7 +186,13 @@ impl Rule for ElemStyleRule {
|
|||
let mut result = Ok(());
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
if !ctx.parser.is_style_registered(style_key.as_str()) {
|
||||
if !ctx
|
||||
.state
|
||||
.shared
|
||||
.styles
|
||||
.borrow()
|
||||
.is_registered(style_key.as_str())
|
||||
{
|
||||
result = Err(BadArgument {
|
||||
to: Some("set".to_string()),
|
||||
pos: 1,
|
||||
|
@ -197,7 +204,7 @@ impl Rule for ElemStyleRule {
|
|||
return;
|
||||
}
|
||||
|
||||
let style = ctx.parser.current_style(style_key.as_str());
|
||||
let style = ctx.state.shared.styles.borrow().current(style_key.as_str());
|
||||
let new_style = match style.from_lua(lua, new_style) {
|
||||
Err(err) => {
|
||||
result = Err(err);
|
||||
|
@ -206,7 +213,7 @@ impl Rule for ElemStyleRule {
|
|||
Ok(new_style) => new_style,
|
||||
};
|
||||
|
||||
ctx.parser.set_current_style(new_style);
|
||||
ctx.state.shared.styles.borrow_mut().set_current(new_style);
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -215,6 +222,6 @@ impl Rule for ElemStyleRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::ops::Range;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Once;
|
||||
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::util::Property;
|
||||
use crate::parser::util::PropertyMapError;
|
||||
use crate::parser::util::PropertyParser;
|
||||
|
@ -15,8 +16,6 @@ use crypto::sha2::Sha512;
|
|||
use graphviz_rust::cmd::Format;
|
||||
use graphviz_rust::cmd::Layout;
|
||||
use graphviz_rust::exec_dot;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
|
||||
|
@ -27,7 +26,6 @@ use crate::compiler::compiler::Target;
|
|||
use crate::document::document::Document;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
|
@ -70,7 +68,7 @@ impl Graphviz {
|
|||
let split_at = out.split_at(svg_start).1.find('\n').unwrap();
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -166,18 +164,14 @@ impl GraphRule {
|
|||
);
|
||||
props.insert(
|
||||
"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 {
|
||||
re: [Regex::new(
|
||||
r"\[graph\](?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\[/graph\])?",
|
||||
)
|
||||
.unwrap()],
|
||||
properties: PropertyParser{ properties: props },
|
||||
properties: PropertyParser { properties: props },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +184,7 @@ impl RegexRule for GraphRule {
|
|||
fn on_regex_match(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -207,10 +201,10 @@ impl RegexRule for GraphRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Missing terminating `{}` after first `{}`",
|
||||
"[/graph]".fg(parser.colors().info),
|
||||
"[graph]".fg(parser.colors().info)
|
||||
"[/graph]".fg(state.parser.colors().info),
|
||||
"[graph]".fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -230,7 +224,7 @@ impl RegexRule for GraphRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), content.range()))
|
||||
.with_message("Graph code is empty")
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -251,7 +245,7 @@ impl RegexRule for GraphRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!("Graph is missing property: {e}"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -269,7 +263,7 @@ impl RegexRule for GraphRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), props.range()))
|
||||
.with_message(e)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -294,10 +288,10 @@ impl RegexRule for GraphRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Property `layout: {}` cannot be converted: {}",
|
||||
prop.fg(parser.colors().info),
|
||||
err.fg(parser.colors().error)
|
||||
prop.fg(state.parser.colors().info),
|
||||
err.fg(state.parser.colors().error)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -313,7 +307,7 @@ impl RegexRule for GraphRule {
|
|||
token.start() + 1..token.end(),
|
||||
))
|
||||
.with_message(err)
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -340,19 +334,19 @@ impl RegexRule for GraphRule {
|
|||
))
|
||||
.with_message(format!(
|
||||
"Property `{}` is missing",
|
||||
err.fg(parser.colors().info)
|
||||
err.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
return reports;
|
||||
}
|
||||
_ => panic!("Unknown error")
|
||||
_ => panic!("Unknown error"),
|
||||
},
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Graphviz {
|
||||
location: token,
|
||||
|
@ -364,7 +358,4 @@ impl RegexRule for GraphRule {
|
|||
|
||||
reports
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::document::document::Document;
|
||||
use crate::document::document::DocumentAccessors;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -10,8 +10,6 @@ use ariadne::Fmt;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
use std::ops::Range;
|
||||
|
@ -48,7 +46,7 @@ impl RegexRule for ImportRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -57,7 +55,7 @@ impl RegexRule for ImportRule {
|
|||
|
||||
// Path
|
||||
let import_file = match matches.get(2) {
|
||||
Some(name) => match ImportRule::validate_name(parser.colors(), name.as_str()) {
|
||||
Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) {
|
||||
Err(msg) => {
|
||||
result.push(
|
||||
Report::build(ReportKind::Error, token.source(), name.start())
|
||||
|
@ -66,9 +64,9 @@ impl RegexRule for ImportRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Import name `{}` is invalid. {msg}",
|
||||
name.as_str().fg(parser.colors().highlight)
|
||||
name.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -85,9 +83,9 @@ impl RegexRule for ImportRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Unable to access file `{}`",
|
||||
filename.fg(parser.colors().highlight)
|
||||
filename.fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -104,9 +102,9 @@ impl RegexRule for ImportRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Path `{}` is not a file!",
|
||||
filename.fg(parser.colors().highlight)
|
||||
filename.fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -121,7 +119,8 @@ impl RegexRule for ImportRule {
|
|||
|
||||
// [Optional] import as
|
||||
let import_as = match matches.get(1) {
|
||||
Some(as_name) => match ImportRule::validate_as(parser.colors(), as_name.as_str()) {
|
||||
Some(as_name) => match ImportRule::validate_as(state.parser.colors(), as_name.as_str())
|
||||
{
|
||||
Ok(as_name) => as_name,
|
||||
Err(msg) => {
|
||||
result.push(
|
||||
|
@ -131,9 +130,9 @@ impl RegexRule for ImportRule {
|
|||
Label::new((token.source(), as_name.range()))
|
||||
.with_message(format!(
|
||||
"Canot import `{import_file}` as `{}`. {msg}",
|
||||
as_name.as_str().fg(parser.colors().highlight)
|
||||
as_name.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -153,7 +152,7 @@ impl RegexRule for ImportRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), token.range))
|
||||
.with_message(format!("Failed to read content from path `{path}`"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -161,12 +160,15 @@ impl RegexRule for ImportRule {
|
|||
}
|
||||
};
|
||||
|
||||
let import_doc = parser.parse(import, Some(document));
|
||||
state.with_state(|new_state| {
|
||||
let (import_doc, _) = new_state.parser.parse(new_state, import, Some(document));
|
||||
document.merge(import_doc.content(), import_doc.scope(), Some(&import_as));
|
||||
});
|
||||
|
||||
// Close paragraph
|
||||
if document.last_element::<Paragraph>().is_some() {
|
||||
parser.push(
|
||||
// TODO2: Check if this is safe to remove
|
||||
if document.last_element::<Paragraph>().is_none() {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Paragraph {
|
||||
location: Token::new(token.end()..token.end(), token.source()),
|
||||
|
@ -177,6 +179,4 @@ impl RegexRule for ImportRule {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
|
|
@ -3,21 +3,21 @@ use crate::compiler::compiler::Target;
|
|||
use crate::document::document::Document;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::document::layout::LayoutType;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::layout::LayoutHolder;
|
||||
use crate::parser::layout::LayoutType;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
use crate::parser::state::RuleState;
|
||||
use crate::parser::state::Scope;
|
||||
use crate::parser::state::State;
|
||||
use crate::parser::util::process_escaped;
|
||||
use ariadne::Fmt;
|
||||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use lazy_static::lazy_static;
|
||||
use mlua::Error::BadArgument;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
|
@ -54,6 +54,7 @@ impl FromStr for LayoutToken {
|
|||
}
|
||||
|
||||
mod default_layouts {
|
||||
use crate::parser::layout::LayoutType;
|
||||
use crate::parser::util::Property;
|
||||
use crate::parser::util::PropertyParser;
|
||||
|
||||
|
@ -242,12 +243,12 @@ struct LayoutState {
|
|||
pub(self) stack: Vec<(Vec<Token>, Rc<dyn LayoutType>)>,
|
||||
}
|
||||
|
||||
impl State for LayoutState {
|
||||
impl RuleState for LayoutState {
|
||||
fn scope(&self) -> Scope { Scope::DOCUMENT }
|
||||
|
||||
fn on_remove<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
@ -265,15 +266,15 @@ impl State for LayoutState {
|
|||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Layout {} stars here",
|
||||
layout_type.name().fg(parser.colors().info)
|
||||
layout_type.name().fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new((at.source(), at.range.clone()))
|
||||
.with_order(2)
|
||||
.with_message("Document ends here".to_string())
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -313,17 +314,17 @@ impl LayoutRule {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn initialize_state(parser: &dyn Parser) -> Rc<RefCell<dyn State>> {
|
||||
let query = parser.state().query(&STATE_NAME);
|
||||
match query {
|
||||
pub fn initialize_state(state: &ParserState) -> Rc<RefCell<dyn RuleState>> {
|
||||
let mut rule_state_borrow = state.shared.rule_state.borrow_mut();
|
||||
match rule_state_borrow.get(STATE_NAME) {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// Insert as a new state
|
||||
match parser.state_mut().insert(
|
||||
STATE_NAME.clone(),
|
||||
match rule_state_borrow.insert(
|
||||
STATE_NAME.into(),
|
||||
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
|
||||
) {
|
||||
Err(_) => panic!("Unknown error"),
|
||||
Err(err) => panic!("{err}"),
|
||||
Ok(state) => state,
|
||||
}
|
||||
}
|
||||
|
@ -373,9 +374,7 @@ impl LayoutRule {
|
|||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref STATE_NAME: String = "elements.layout".to_string();
|
||||
}
|
||||
static STATE_NAME: &'static str = "elements.layout";
|
||||
|
||||
impl RegexRule for LayoutRule {
|
||||
fn name(&self) -> &'static str { "Layout" }
|
||||
|
@ -385,14 +384,14 @@ impl RegexRule for LayoutRule {
|
|||
fn on_regex_match(
|
||||
&self,
|
||||
index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
||||
let state = LayoutRule::initialize_state(parser);
|
||||
let rule_state = LayoutRule::initialize_state(state);
|
||||
|
||||
if index == 0
|
||||
// BEGIN_LAYOUT
|
||||
|
@ -406,9 +405,9 @@ impl RegexRule for LayoutRule {
|
|||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Missing layout name after `{}`",
|
||||
"#+BEGIN_LAYOUT".fg(parser.colors().highlight)
|
||||
"#+BEGIN_LAYOUT".fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -426,9 +425,9 @@ impl RegexRule for LayoutRule {
|
|||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Empty layout name after `{}`",
|
||||
"#+BEGIN_LAYOUT".fg(parser.colors().highlight)
|
||||
"#+BEGIN_LAYOUT".fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -443,9 +442,9 @@ impl RegexRule for LayoutRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Missing a space before layout name `{}`",
|
||||
name.as_str().fg(parser.colors().highlight)
|
||||
name.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -453,7 +452,7 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
|
||||
// Get layout
|
||||
let layout_type = match parser.get_layout(trimmed) {
|
||||
let layout_type = match state.shared.layouts.borrow().get(trimmed) {
|
||||
None => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), name.start())
|
||||
|
@ -462,9 +461,9 @@ impl RegexRule for LayoutRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Cannot find layout `{}`",
|
||||
trimmed.fg(parser.colors().highlight)
|
||||
trimmed.fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -475,7 +474,7 @@ impl RegexRule for LayoutRule {
|
|||
|
||||
// Parse properties
|
||||
let properties = match LayoutRule::parse_properties(
|
||||
parser.colors(),
|
||||
state.parser.colors(),
|
||||
&token,
|
||||
layout_type.clone(),
|
||||
matches.get(1),
|
||||
|
@ -487,7 +486,7 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Layout {
|
||||
location: token.clone(),
|
||||
|
@ -498,11 +497,12 @@ impl RegexRule for LayoutRule {
|
|||
}),
|
||||
);
|
||||
|
||||
state
|
||||
rule_state
|
||||
.as_ref()
|
||||
.borrow_mut()
|
||||
.downcast_mut::<LayoutState>()
|
||||
.map_or_else(
|
||||
|| panic!("Invalid state at: `{}`", STATE_NAME.as_str()),
|
||||
|| panic!("Invalid state at: `{STATE_NAME}`"),
|
||||
|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
|
||||
// LAYOUT_NEXT
|
||||
{
|
||||
let mut state_borrow = state.borrow_mut();
|
||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
let mut rule_state_borrow = rule_state.as_ref().borrow_mut();
|
||||
let layout_state = rule_state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
|
||||
let (tokens, layout_type) = match state.stack.last_mut() {
|
||||
let (tokens, layout_type) = match layout_state.stack.last_mut() {
|
||||
None => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), token.start())
|
||||
|
@ -524,7 +524,7 @@ impl RegexRule for LayoutRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message("No active layout found".to_string())
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -543,10 +543,10 @@ impl RegexRule for LayoutRule {
|
|||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Layout expects a maximum of {} blocks, currently at {}",
|
||||
layout_type.expects().end.fg(parser.colors().info),
|
||||
tokens.len().fg(parser.colors().info),
|
||||
layout_type.expects().end.fg(state.parser.colors().info),
|
||||
tokens.len().fg(state.parser.colors().info),
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -555,7 +555,7 @@ impl RegexRule for LayoutRule {
|
|||
|
||||
// Parse properties
|
||||
let properties = match LayoutRule::parse_properties(
|
||||
parser.colors(),
|
||||
state.parser.colors(),
|
||||
&token,
|
||||
layout_type.clone(),
|
||||
matches.get(1),
|
||||
|
@ -576,10 +576,10 @@ impl RegexRule for LayoutRule {
|
|||
)
|
||||
} else {
|
||||
// LAYOUT_END
|
||||
let mut state_borrow = state.borrow_mut();
|
||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
let mut rule_state_borrow = rule_state.as_ref().borrow_mut();
|
||||
let layout_state = rule_state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
|
||||
let (tokens, layout_type) = match state.stack.last_mut() {
|
||||
let (tokens, layout_type) = match layout_state.stack.last_mut() {
|
||||
None => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), token.start())
|
||||
|
@ -587,7 +587,7 @@ impl RegexRule for LayoutRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message("No active layout found".to_string())
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -606,10 +606,10 @@ impl RegexRule for LayoutRule {
|
|||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Layout expects a minimum of {} blocks, currently at {}",
|
||||
layout_type.expects().start.fg(parser.colors().info),
|
||||
tokens.len().fg(parser.colors().info),
|
||||
layout_type.expects().start.fg(state.parser.colors().info),
|
||||
tokens.len().fg(state.parser.colors().info),
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -618,7 +618,7 @@ impl RegexRule for LayoutRule {
|
|||
|
||||
// Parse properties
|
||||
let properties = match LayoutRule::parse_properties(
|
||||
parser.colors(),
|
||||
state.parser.colors(),
|
||||
&token,
|
||||
layout_type.clone(),
|
||||
matches.get(1),
|
||||
|
@ -632,11 +632,11 @@ impl RegexRule for LayoutRule {
|
|||
|
||||
let layout_type = layout_type.clone();
|
||||
let id = tokens.len();
|
||||
state.stack.pop();
|
||||
layout_state.stack.pop();
|
||||
(id, LayoutToken::End, layout_type, properties)
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Layout {
|
||||
location: token,
|
||||
|
@ -650,8 +650,8 @@ impl RegexRule for LayoutRule {
|
|||
return reports;
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
// TODO: Add method to create new layouts
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
|
@ -676,11 +676,12 @@ impl RegexRule for LayoutRule {
|
|||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
// Make sure the state has been initialized
|
||||
let state = LayoutRule::initialize_state(ctx.parser);
|
||||
// Make sure the rule state has been initialized
|
||||
let rule_state = LayoutRule::initialize_state(ctx.state);
|
||||
|
||||
// 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 => {
|
||||
result = Err(BadArgument {
|
||||
|
@ -712,7 +713,7 @@ impl RegexRule for LayoutRule {
|
|||
|
||||
let id = match layout_token {
|
||||
LayoutToken::Begin => {
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Layout {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -723,20 +724,21 @@ impl RegexRule for LayoutRule {
|
|||
}),
|
||||
);
|
||||
|
||||
state
|
||||
rule_state
|
||||
.as_ref()
|
||||
.borrow_mut()
|
||||
.downcast_mut::<LayoutState>()
|
||||
.map_or_else(
|
||||
|| panic!("Invalid state at: `{}`", STATE_NAME.as_str()),
|
||||
|| panic!("Invalid state at: `{STATE_NAME}`"),
|
||||
|s| s.stack.push((vec![ctx.location.clone()], layout_type.clone())),
|
||||
);
|
||||
return;
|
||||
},
|
||||
LayoutToken::Next => {
|
||||
let mut state_borrow = state.borrow_mut();
|
||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
let mut state_borrow = rule_state.as_ref().borrow_mut();
|
||||
let layout_state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
|
||||
let (tokens, current_layout_type) = match state.stack.last_mut() {
|
||||
let (tokens, current_layout_type) = match layout_state.stack.last_mut() {
|
||||
None => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
|
@ -781,10 +783,10 @@ impl RegexRule for LayoutRule {
|
|||
tokens.len() - 1
|
||||
},
|
||||
LayoutToken::End => {
|
||||
let mut state_borrow = state.borrow_mut();
|
||||
let state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
let mut state_borrow = rule_state.as_ref().borrow_mut();
|
||||
let layout_state = state_borrow.downcast_mut::<LayoutState>().unwrap();
|
||||
|
||||
let (tokens, current_layout_type) = match state.stack.last_mut() {
|
||||
let (tokens, current_layout_type) = match layout_state.stack.last_mut() {
|
||||
None => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
|
@ -826,12 +828,12 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
|
||||
let id = tokens.len();
|
||||
state.stack.pop();
|
||||
layout_state.stack.pop();
|
||||
id
|
||||
}
|
||||
};
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Layout {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -850,12 +852,12 @@ impl RegexRule for LayoutRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
|
||||
fn register_layouts(&self, parser: &dyn Parser) {
|
||||
parser.insert_layout(Rc::new(default_layouts::Centered::default()));
|
||||
parser.insert_layout(Rc::new(default_layouts::Split::default()));
|
||||
fn register_layouts(&self, holder: &mut LayoutHolder) {
|
||||
holder.insert(Rc::new(default_layouts::Centered::default()));
|
||||
holder.insert(Rc::new(default_layouts::Split::default()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -864,6 +866,7 @@ mod tests {
|
|||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -892,7 +895,7 @@ mod tests {
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Layout { token == LayoutToken::Begin, id == 0 };
|
||||
|
@ -944,7 +947,7 @@ mod tests {
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Layout { token == LayoutToken::Begin, id == 0 };
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::document::element::ContainerElement;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
|
@ -91,7 +91,7 @@ impl RegexRule for LinkRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -107,7 +107,7 @@ impl RegexRule for LinkRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), display.range()))
|
||||
.with_message("Link name is empty")
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -122,10 +122,10 @@ impl RegexRule for LinkRule {
|
|||
Label::new((token.source(), display.range()))
|
||||
.with_message(format!(
|
||||
"Link name is empty. Once processed, `{}` yields `{}`",
|
||||
display.as_str().fg(parser.colors().highlight),
|
||||
processed.fg(parser.colors().highlight),
|
||||
display.as_str().fg(state.parser.colors().highlight),
|
||||
processed.fg(state.parser.colors().highlight),
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -137,7 +137,7 @@ impl RegexRule for LinkRule {
|
|||
"Link Display".to_string(),
|
||||
processed,
|
||||
));
|
||||
match util::parse_paragraph(parser, source, document) {
|
||||
match util::parse_paragraph(state, source, document) {
|
||||
Err(err) => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), display.start())
|
||||
|
@ -145,7 +145,7 @@ impl RegexRule for LinkRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), display.range()))
|
||||
.with_message(err.to_string())
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -166,7 +166,7 @@ impl RegexRule for LinkRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), url.range()))
|
||||
.with_message("Link url is empty")
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -182,10 +182,10 @@ impl RegexRule for LinkRule {
|
|||
Label::new((token.source(), url.range()))
|
||||
.with_message(format!(
|
||||
"Link url is empty. Once processed, `{}` yields `{}`",
|
||||
url.as_str().fg(parser.colors().highlight),
|
||||
text_content.as_str().fg(parser.colors().highlight),
|
||||
url.as_str().fg(state.parser.colors().highlight),
|
||||
text_content.as_str().fg(state.parser.colors().highlight),
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -196,7 +196,7 @@ impl RegexRule for LinkRule {
|
|||
_ => panic!("Empty link url"),
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Link {
|
||||
location: token,
|
||||
|
@ -208,7 +208,7 @@ impl RegexRule for LinkRule {
|
|||
return reports;
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
|
@ -223,7 +223,7 @@ impl RegexRule for LinkRule {
|
|||
display,
|
||||
));
|
||||
let display_content =
|
||||
match util::parse_paragraph(ctx.parser, source, ctx.document) {
|
||||
match util::parse_paragraph(ctx.state, source, ctx.document) {
|
||||
Err(err) => {
|
||||
result = Err(BadArgument {
|
||||
to: Some("push".to_string()),
|
||||
|
@ -240,7 +240,7 @@ impl RegexRule for LinkRule {
|
|||
}
|
||||
};
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Link {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -256,7 +256,7 @@ impl RegexRule for LinkRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,6 +266,7 @@ mod tests {
|
|||
use crate::elements::style::Style;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -283,7 +284,7 @@ Some [link](url).
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
@ -313,7 +314,7 @@ nml.link.push("**BOLD link**", "another url")
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::document::document::DocumentAccessors;
|
|||
use crate::document::element::ContainerElement;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::Rule;
|
||||
use crate::parser::source::Cursor;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -25,8 +25,6 @@ use crate::parser::util::PropertyParser;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Match;
|
||||
use regex::Regex;
|
||||
|
||||
|
@ -136,7 +134,7 @@ impl ListRule {
|
|||
|
||||
fn push_markers(
|
||||
token: &Token,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
current: &Vec<(bool, usize)>,
|
||||
target: &Vec<(bool, usize)>,
|
||||
|
@ -152,7 +150,7 @@ impl ListRule {
|
|||
|
||||
// Close
|
||||
for i in start_pos..current.len() {
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListMarker {
|
||||
location: token.clone(),
|
||||
|
@ -164,7 +162,7 @@ impl ListRule {
|
|||
|
||||
// Open
|
||||
for i in start_pos..target.len() {
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListMarker {
|
||||
location: token.clone(),
|
||||
|
@ -252,7 +250,7 @@ impl ListRule {
|
|||
impl Rule for ListRule {
|
||||
fn name(&self) -> &'static str { "List" }
|
||||
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
self.start_re
|
||||
.find_at(cursor.source.content(), cursor.pos)
|
||||
.map_or(None, |m| {
|
||||
|
@ -262,10 +260,10 @@ impl Rule for ListRule {
|
|||
|
||||
fn on_match<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
cursor: Cursor,
|
||||
_match_data: Option<Box<dyn Any>>,
|
||||
_match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
let mut reports = vec![];
|
||||
|
||||
|
@ -295,7 +293,7 @@ impl Rule for ListRule {
|
|||
.with_label(
|
||||
Label::new((cursor.source.clone(), properties.range()))
|
||||
.with_message(err)
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -354,12 +352,12 @@ impl Rule for ListRule {
|
|||
captures.get(1).unwrap().range(),
|
||||
))
|
||||
.with_message("Spacing for list entries do not match")
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.with_label(
|
||||
Label::new((cursor.source.clone(), spacing.0.clone()))
|
||||
.with_message("Previous spacing")
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -379,7 +377,7 @@ impl Rule for ListRule {
|
|||
"List Entry".to_string(),
|
||||
entry_content,
|
||||
));
|
||||
let parsed_content = match util::parse_paragraph(parser, entry_src, document) {
|
||||
let parsed_content = match util::parse_paragraph(state, entry_src, document) {
|
||||
Err(err) => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Warning, token.source(), token.range.start)
|
||||
|
@ -387,7 +385,7 @@ impl Rule for ListRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), token.range.clone()))
|
||||
.with_message(err)
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -400,12 +398,12 @@ impl Rule for ListRule {
|
|||
.last_element::<ListEntry>()
|
||||
.map(|ent| ent.numbering.clone())
|
||||
{
|
||||
ListRule::push_markers(&token, parser, document, &previous_depth, &depth);
|
||||
ListRule::push_markers(&token, state, document, &previous_depth, &depth);
|
||||
} else {
|
||||
ListRule::push_markers(&token, parser, document, &vec![], &depth);
|
||||
ListRule::push_markers(&token, state, document, &vec![], &depth);
|
||||
}
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListEntry {
|
||||
location: Token::new(
|
||||
|
@ -428,13 +426,10 @@ impl Rule for ListRule {
|
|||
.map(|ent| ent.numbering.clone())
|
||||
.unwrap();
|
||||
let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone());
|
||||
ListRule::push_markers(&token, parser, document, ¤t, &Vec::new());
|
||||
ListRule::push_markers(&token, state, document, ¤t, &Vec::new());
|
||||
|
||||
(end_cursor, reports)
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -443,6 +438,7 @@ mod tests {
|
|||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -466,7 +462,8 @@ mod tests {
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let state = ParserState::new(&parser, None);
|
||||
let (doc, _) = parser.parse(state, source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
ListMarker { numbered == false, kind == MarkerKind::Open };
|
||||
|
|
|
@ -7,8 +7,6 @@ use ariadne::Fmt;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Match;
|
||||
use regex::Regex;
|
||||
|
@ -23,7 +21,7 @@ use crate::document::element::ElemKind;
|
|||
use crate::document::element::Element;
|
||||
use crate::document::element::ReferenceableElement;
|
||||
use crate::document::references::validate_refname;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -254,7 +252,7 @@ impl MediaRule {
|
|||
.multi_line(true)
|
||||
.build()
|
||||
.unwrap()],
|
||||
properties: PropertyParser{ properties: props },
|
||||
properties: PropertyParser { properties: props },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,7 +331,7 @@ impl RegexRule for MediaRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -377,7 +375,8 @@ impl RegexRule for MediaRule {
|
|||
};
|
||||
|
||||
// Properties
|
||||
let properties = match self.parse_properties(parser.colors(), &token, &matches.get(3)) {
|
||||
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3))
|
||||
{
|
||||
Ok(pm) => pm,
|
||||
Err(report) => {
|
||||
reports.push(report);
|
||||
|
@ -401,10 +400,10 @@ impl RegexRule for MediaRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Property `type: {}` cannot be converted: {}",
|
||||
prop.fg(parser.colors().info),
|
||||
err.fg(parser.colors().error)
|
||||
prop.fg(state.parser.colors().info),
|
||||
err.fg(state.parser.colors().error)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -420,7 +419,7 @@ impl RegexRule for MediaRule {
|
|||
token.start() + 1..token.end(),
|
||||
))
|
||||
.with_message(format!("{err}. Required because mediatype could not be detected"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -454,7 +453,7 @@ impl RegexRule for MediaRule {
|
|||
if source.content().is_empty() {
|
||||
None
|
||||
} else {
|
||||
match parse_paragraph(parser, source, document) {
|
||||
match parse_paragraph(state, source, document) {
|
||||
Ok(paragraph) => Some(*paragraph),
|
||||
Err(err) => {
|
||||
reports.push(
|
||||
|
@ -465,7 +464,7 @@ impl RegexRule for MediaRule {
|
|||
.with_message(format!(
|
||||
"Could not parse description: {err}"
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -480,7 +479,7 @@ impl RegexRule for MediaRule {
|
|||
let mut group = match document.last_element_mut::<Media>() {
|
||||
Some(group) => group,
|
||||
None => {
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Media {
|
||||
location: token.clone(),
|
||||
|
@ -507,7 +506,7 @@ impl RegexRule for MediaRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(err)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -515,13 +514,12 @@ impl RegexRule for MediaRule {
|
|||
|
||||
reports
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
|
||||
use super::*;
|
||||
|
@ -550,7 +548,7 @@ mod tests {
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
let borrow = doc.content().borrow();
|
||||
let group = borrow.first().as_ref().unwrap().as_container().unwrap();
|
||||
|
|
|
@ -3,8 +3,6 @@ use std::ops::Range;
|
|||
use std::rc::Rc;
|
||||
|
||||
use ariadne::Report;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::compiler::compiler::Compiler;
|
||||
|
@ -13,7 +11,7 @@ use crate::document::document::Document;
|
|||
use crate::document::element::ContainerElement;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::Rule;
|
||||
use crate::parser::source::Cursor;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -108,7 +106,7 @@ impl ParagraphRule {
|
|||
impl Rule for ParagraphRule {
|
||||
fn name(&self) -> &'static str { "Paragraphing" }
|
||||
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
self.re
|
||||
.find_at(cursor.source.content(), cursor.pos)
|
||||
.and_then(|m| Some((m.start(), Box::new([false; 0]) as Box<dyn Any>)))
|
||||
|
@ -116,17 +114,17 @@ impl Rule for ParagraphRule {
|
|||
|
||||
fn on_match(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
cursor: Cursor,
|
||||
_match_data: Option<Box<dyn Any>>,
|
||||
_match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
let end_cursor = match self.re.captures_at(cursor.source.content(), cursor.pos) {
|
||||
None => panic!("Unknown error"),
|
||||
Some(capture) => cursor.at(capture.get(0).unwrap().end() - 1),
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Paragraph {
|
||||
location: Token::new(cursor.pos..end_cursor.pos, cursor.source.clone()),
|
||||
|
@ -136,9 +134,6 @@ impl Rule for ParagraphRule {
|
|||
|
||||
(end_cursor, Vec::new())
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -146,6 +141,7 @@ mod tests {
|
|||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -169,7 +165,7 @@ Last paragraph
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::document::document::Document;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
|
@ -78,7 +78,7 @@ impl RegexRule for RawRule {
|
|||
fn on_regex_match(
|
||||
&self,
|
||||
_index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -95,10 +95,10 @@ impl RegexRule for RawRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Missing terminating `{}` after first `{}`",
|
||||
"?}".fg(parser.colors().info),
|
||||
"{?".fg(parser.colors().info)
|
||||
"?}".fg(state.parser.colors().info),
|
||||
"{?".fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -115,7 +115,7 @@ impl RegexRule for RawRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), content.range()))
|
||||
.with_message("Raw code is empty")
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -134,7 +134,7 @@ impl RegexRule for RawRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!("Raw code is missing properties: {e}"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -152,7 +152,7 @@ impl RegexRule for RawRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), props.range()))
|
||||
.with_message(e)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -176,10 +176,10 @@ impl RegexRule for RawRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Property `kind: {}` cannot be converted: {}",
|
||||
prop.fg(parser.colors().info),
|
||||
err.fg(parser.colors().error)
|
||||
prop.fg(state.parser.colors().info),
|
||||
err.fg(state.parser.colors().error)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -196,9 +196,9 @@ impl RegexRule for RawRule {
|
|||
))
|
||||
.with_message(format!(
|
||||
"Property `{}` is missing",
|
||||
err.fg(parser.colors().info)
|
||||
err.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -207,7 +207,7 @@ impl RegexRule for RawRule {
|
|||
},
|
||||
};
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Raw {
|
||||
location: token.clone(),
|
||||
|
@ -219,7 +219,7 @@ impl RegexRule for RawRule {
|
|||
reports
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
|
@ -242,7 +242,7 @@ impl RegexRule for RawRule {
|
|||
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Raw {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -258,7 +258,7 @@ impl RegexRule for RawRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,7 +268,8 @@ mod tests {
|
|||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
#[test]
|
||||
|
@ -282,7 +283,7 @@ Break{?[kind=block] Raw?}NewParagraph{?<b>?}
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph;
|
||||
|
@ -305,7 +306,7 @@ Break%<nml.raw.push("block", "Raw")>%NewParagraph%<nml.raw.push("inline", "<b>")
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph;
|
||||
|
|
|
@ -6,8 +6,6 @@ use ariadne::Fmt;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Match;
|
||||
use regex::Regex;
|
||||
|
@ -18,7 +16,7 @@ use crate::document::document::Document;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::document::references::validate_refname;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -82,7 +80,7 @@ impl ReferenceRule {
|
|||
);
|
||||
Self {
|
||||
re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
|
||||
properties: PropertyParser{ properties: props },
|
||||
properties: PropertyParser { properties: props },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +133,7 @@ impl RegexRule for ReferenceRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -155,7 +153,7 @@ impl RegexRule for ReferenceRule {
|
|||
Label::new((token.source().clone(), m.range())).with_message(
|
||||
format!(
|
||||
"Could not find element with reference: `{}`",
|
||||
refname.fg(parser.colors().info)
|
||||
refname.fg(state.parser.colors().info)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -178,7 +176,8 @@ impl RegexRule for ReferenceRule {
|
|||
}
|
||||
};
|
||||
// Properties
|
||||
let properties = match self.parse_properties(parser.colors(), &token, &matches.get(3)) {
|
||||
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3))
|
||||
{
|
||||
Ok(pm) => pm,
|
||||
Err(report) => {
|
||||
reports.push(report);
|
||||
|
@ -193,7 +192,7 @@ impl RegexRule for ReferenceRule {
|
|||
.ok()
|
||||
.and_then(|(_, s)| Some(s));
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Reference {
|
||||
location: token,
|
||||
|
@ -204,6 +203,4 @@ impl RegexRule for ReferenceRule {
|
|||
|
||||
reports
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserStrategy;
|
||||
|
||||
use super::code::CodeRule;
|
||||
use super::comment::CommentRule;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::document::document::Document;
|
||||
use crate::lua::kernel::Kernel;
|
||||
use crate::lua::kernel::KernelContext;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -12,7 +12,6 @@ use ariadne::Fmt;
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
|
@ -84,7 +83,7 @@ impl RegexRule for ScriptRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -93,7 +92,8 @@ impl RegexRule for ScriptRule {
|
|||
|
||||
let kernel_name = match matches.get(1) {
|
||||
None => "main".to_string(),
|
||||
Some(name) => match ScriptRule::validate_kernel_name(parser.colors(), name.as_str()) {
|
||||
Some(name) => {
|
||||
match ScriptRule::validate_kernel_name(state.parser.colors(), name.as_str()) {
|
||||
Ok(name) => name,
|
||||
Err(e) => {
|
||||
reports.push(
|
||||
|
@ -102,17 +102,23 @@ impl RegexRule for ScriptRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), name.range()))
|
||||
.with_message(e)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
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
|
||||
.get(if index == 0 { 2 } else { 3 })
|
||||
|
@ -127,7 +133,7 @@ impl RegexRule for ScriptRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), token.start() + 1..token.end()))
|
||||
.with_message("Kernel code is empty")
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -166,7 +172,7 @@ impl RegexRule for ScriptRule {
|
|||
"Kernel execution failed:\n{}",
|
||||
e.to_string()
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -178,7 +184,7 @@ impl RegexRule for ScriptRule {
|
|||
// Validate kind
|
||||
let kind = match matches.get(2) {
|
||||
None => 0,
|
||||
Some(kind) => match self.validate_kind(parser.colors(), kind.as_str()) {
|
||||
Some(kind) => match self.validate_kind(state.parser.colors(), kind.as_str()) {
|
||||
Ok(kind) => kind,
|
||||
Err(msg) => {
|
||||
reports.push(
|
||||
|
@ -187,7 +193,7 @@ impl RegexRule for ScriptRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), kind.range()))
|
||||
.with_message(msg)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -209,7 +215,7 @@ impl RegexRule for ScriptRule {
|
|||
"Kernel evaluation failed:\n{}",
|
||||
e.to_string()
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -223,7 +229,7 @@ impl RegexRule for ScriptRule {
|
|||
// Eval to text
|
||||
{
|
||||
if !result.is_empty() {
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Text::new(
|
||||
Token::new(1..source.content().len(), source.clone()),
|
||||
|
@ -240,7 +246,11 @@ impl RegexRule for ScriptRule {
|
|||
result,
|
||||
)) as Rc<dyn Source>;
|
||||
|
||||
parser.parse_into(parse_source, document);
|
||||
state.with_state(|new_state| {
|
||||
new_state
|
||||
.parser
|
||||
.parse_into(new_state, parse_source, document);
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -253,7 +263,7 @@ impl RegexRule for ScriptRule {
|
|||
"Kernel evaluation failed:\n{}",
|
||||
e.to_string()
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -267,15 +277,12 @@ impl RegexRule for ScriptRule {
|
|||
|
||||
let ctx = KernelContext {
|
||||
location: Token::new(0..source.content().len(), source.clone()),
|
||||
parser,
|
||||
state,
|
||||
document,
|
||||
};
|
||||
|
||||
kernel.run_with_context(ctx, execute)
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -287,6 +294,7 @@ mod tests {
|
|||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::elements::style::Style;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -312,7 +320,7 @@ Evaluation: %<! make_ref("hello", "id")>%
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph;
|
||||
|
|
|
@ -5,10 +5,11 @@ use crate::document::element::ElemKind;
|
|||
use crate::document::element::Element;
|
||||
use crate::document::element::ReferenceableElement;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
use crate::parser::style::StyleHolder;
|
||||
use ariadne::Fmt;
|
||||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
|
@ -160,7 +161,7 @@ impl RegexRule for SectionRule {
|
|||
fn on_regex_match(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
matches: regex::Captures,
|
||||
|
@ -175,9 +176,9 @@ impl RegexRule for SectionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), depth.range()))
|
||||
.with_message(format!("Section is of depth {}, which is greather than {} (maximum depth allowed)",
|
||||
depth.len().fg(parser.colors().info),
|
||||
6.fg(parser.colors().info)))
|
||||
.with_color(parser.colors().error))
|
||||
depth.len().fg(state.parser.colors().info),
|
||||
6.fg(state.parser.colors().info)))
|
||||
.with_color(state.parser.colors().error))
|
||||
.finish());
|
||||
return result;
|
||||
}
|
||||
|
@ -202,17 +203,17 @@ impl RegexRule for SectionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), refname.range()))
|
||||
.with_message(format!("Reference with name `{}` is already defined in `{}`",
|
||||
refname.as_str().fg(parser.colors().highlight),
|
||||
elem.location().source().name().as_str().fg(parser.colors().highlight)))
|
||||
refname.as_str().fg(state.parser.colors().highlight),
|
||||
elem.location().source().name().as_str().fg(state.parser.colors().highlight)))
|
||||
.with_message(format!("`{}` conflicts with previously defined reference to {}",
|
||||
refname.as_str().fg(parser.colors().highlight),
|
||||
elem.element_name().fg(parser.colors().highlight)))
|
||||
.with_color(parser.colors().warning))
|
||||
refname.as_str().fg(state.parser.colors().highlight),
|
||||
elem.element_name().fg(state.parser.colors().highlight)))
|
||||
.with_color(state.parser.colors().warning))
|
||||
.with_label(
|
||||
Label::new((elem.location().source(), elem.location().start()..elem.location().end() ))
|
||||
.with_message(format!("`{}` previously defined here",
|
||||
refname.as_str().fg(parser.colors().highlight)))
|
||||
.with_color(parser.colors().warning))
|
||||
refname.as_str().fg(state.parser.colors().highlight)))
|
||||
.with_color(state.parser.colors().warning))
|
||||
.with_note(format!("Previous reference was overwritten"))
|
||||
.finish());
|
||||
}
|
||||
|
@ -234,10 +235,10 @@ impl RegexRule for SectionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), kind.range()))
|
||||
.with_message(format!("Section numbering kind must be a combination of `{}` for unnumbered, and `{}` for non-listing; got `{}`",
|
||||
"*".fg(parser.colors().info),
|
||||
"+".fg(parser.colors().info),
|
||||
kind.as_str().fg(parser.colors().highlight)))
|
||||
.with_color(parser.colors().error))
|
||||
"*".fg(state.parser.colors().info),
|
||||
"+".fg(state.parser.colors().info),
|
||||
kind.as_str().fg(state.parser.colors().highlight)))
|
||||
.with_color(state.parser.colors().error))
|
||||
.with_help(format!("Leave empty for a numbered listed section"))
|
||||
.finish());
|
||||
return result;
|
||||
|
@ -265,7 +266,7 @@ impl RegexRule for SectionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), name.range()))
|
||||
.with_message("Sections require a name before line end")
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -280,8 +281,8 @@ impl RegexRule for SectionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), name.range()))
|
||||
.with_message("Sections require at least one whitespace before the section's name")
|
||||
.with_color(parser.colors().warning))
|
||||
.with_help(format!("Add a space before `{}`", section_name.fg(parser.colors().highlight)))
|
||||
.with_color(state.parser.colors().warning))
|
||||
.with_help(format!("Add a space before `{}`", section_name.fg(state.parser.colors().highlight)))
|
||||
.finish());
|
||||
return result;
|
||||
}
|
||||
|
@ -292,12 +293,15 @@ impl RegexRule for SectionRule {
|
|||
};
|
||||
|
||||
// Get style
|
||||
let style = parser
|
||||
.current_style(section_style::STYLE_KEY)
|
||||
let style = state
|
||||
.shared
|
||||
.styles
|
||||
.borrow()
|
||||
.current(section_style::STYLE_KEY)
|
||||
.downcast_rc::<SectionStyle>()
|
||||
.unwrap();
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Section {
|
||||
location: token.clone(),
|
||||
|
@ -312,7 +316,7 @@ impl RegexRule for SectionRule {
|
|||
return result;
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
|
||||
bindings.push((
|
||||
|
@ -340,12 +344,15 @@ impl RegexRule for SectionRule {
|
|||
ctx.as_ref().map(|ctx| {
|
||||
// Get style
|
||||
let style = ctx
|
||||
.parser
|
||||
.current_style(section_style::STYLE_KEY)
|
||||
.state
|
||||
.shared
|
||||
.styles
|
||||
.borrow()
|
||||
.current(section_style::STYLE_KEY)
|
||||
.downcast_rc::<SectionStyle>()
|
||||
.unwrap();
|
||||
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Section {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -365,11 +372,11 @@ impl RegexRule for SectionRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
|
||||
fn register_styles(&self, parser: &dyn Parser) {
|
||||
parser.set_current_style(Rc::new(SectionStyle::default()));
|
||||
fn register_styles(&self, holder: &mut StyleHolder) {
|
||||
holder.set_current(Rc::new(SectionStyle::default()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,7 +384,6 @@ mod section_style {
|
|||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::document::style::ElementStyle;
|
||||
use crate::impl_elementstyle;
|
||||
|
||||
pub static STYLE_KEY: &'static str = "style.section";
|
||||
|
@ -409,8 +415,8 @@ mod section_style {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::document::style::StyleHolder;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -432,7 +438,7 @@ mod tests {
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Section { depth == 1, title == "1" };
|
||||
|
@ -462,7 +468,7 @@ nml.section.push("6", 6, "", "refname")
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Section { depth == 1, title == "1" };
|
||||
|
@ -488,10 +494,13 @@ nml.section.push("6", 6, "", "refname")
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let _ = parser.parse(source, None);
|
||||
let state = ParserState::new(&parser, None);
|
||||
let (_, state) = parser.parse(state, source, None);
|
||||
|
||||
let style = parser
|
||||
.current_style(section_style::STYLE_KEY)
|
||||
let style = state.shared
|
||||
.styles
|
||||
.borrow()
|
||||
.current(section_style::STYLE_KEY)
|
||||
.downcast_rc::<SectionStyle>()
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -4,19 +4,16 @@ use crate::document::document::Document;
|
|||
use crate::document::document::DocumentAccessors;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::Token;
|
||||
use crate::parser::state::RuleState;
|
||||
use crate::parser::state::Scope;
|
||||
use crate::parser::state::State;
|
||||
use ariadne::Fmt;
|
||||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use ariadne::ReportKind;
|
||||
use lazy_static::lazy_static;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
use std::cell::RefCell;
|
||||
|
@ -77,12 +74,12 @@ impl StyleState {
|
|||
}
|
||||
}
|
||||
|
||||
impl State for StyleState {
|
||||
impl RuleState for StyleState {
|
||||
fn scope(&self) -> Scope { Scope::PARAGRAPH }
|
||||
|
||||
fn on_remove<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
@ -116,15 +113,15 @@ impl State for StyleState {
|
|||
.with_order(1)
|
||||
.with_message(format!(
|
||||
"Style {} starts here",
|
||||
name.fg(parser.colors().info)
|
||||
name.fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_label(
|
||||
Label::new(paragraph_end)
|
||||
.with_order(1)
|
||||
.with_message(format!("Paragraph ends here"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_note("Styles cannot span multiple documents (i.e @import)")
|
||||
.finish(),
|
||||
|
@ -156,9 +153,7 @@ impl StyleRule {
|
|||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref STATE_NAME: String = "elements.style".to_string();
|
||||
}
|
||||
static STATE_NAME: &'static str = "elements.style";
|
||||
|
||||
impl RegexRule for StyleRule {
|
||||
fn name(&self) -> &'static str { "Style" }
|
||||
|
@ -168,19 +163,21 @@ impl RegexRule for StyleRule {
|
|||
fn on_regex_match(
|
||||
&self,
|
||||
index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
_matches: Captures,
|
||||
) -> Vec<Report<(Rc<dyn Source>, Range<usize>)>> {
|
||||
let query = parser.state().query(&STATE_NAME);
|
||||
let state = match query {
|
||||
let query = state.shared.rule_state.borrow().get(STATE_NAME);
|
||||
let style_state = match query {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// Insert as a new state
|
||||
match parser
|
||||
.state_mut()
|
||||
.insert(STATE_NAME.clone(), Rc::new(RefCell::new(StyleState::new())))
|
||||
match state
|
||||
.shared
|
||||
.rule_state
|
||||
.borrow_mut()
|
||||
.insert(STATE_NAME.into(), Rc::new(RefCell::new(StyleState::new())))
|
||||
{
|
||||
Err(_) => panic!("Unknown error"),
|
||||
Ok(state) => state,
|
||||
|
@ -188,11 +185,11 @@ impl RegexRule for StyleRule {
|
|||
}
|
||||
};
|
||||
|
||||
if let Some(style_state) = state.borrow_mut().downcast_mut::<StyleState>() {
|
||||
if let Some(style_state) = style_state.borrow_mut().downcast_mut::<StyleState>() {
|
||||
style_state.toggled[index] = style_state.toggled[index]
|
||||
.clone()
|
||||
.map_or(Some(token.clone()), |_| None);
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Style::new(
|
||||
token.clone(),
|
||||
|
@ -201,20 +198,18 @@ impl RegexRule for StyleRule {
|
|||
)),
|
||||
);
|
||||
} else {
|
||||
panic!("Invalid state at `{}`", STATE_NAME.as_str());
|
||||
panic!("Invalid state at `{STATE_NAME}`");
|
||||
}
|
||||
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::elements::text::Text;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -235,7 +230,7 @@ __`UNDERLINE+EM`__
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
|
|
@ -14,8 +14,6 @@ use ariadne::Report;
|
|||
use ariadne::ReportKind;
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha512;
|
||||
use mlua::Function;
|
||||
use mlua::Lua;
|
||||
use regex::Captures;
|
||||
use regex::Match;
|
||||
use regex::Regex;
|
||||
|
@ -27,7 +25,7 @@ use crate::compiler::compiler::Target;
|
|||
use crate::document::document::Document;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -304,7 +302,7 @@ impl RegexRule for TexRule {
|
|||
fn on_regex_match(
|
||||
&self,
|
||||
index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
|
@ -321,10 +319,10 @@ impl RegexRule for TexRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Missing terminating `{}` after first `{}`",
|
||||
["|$", "$"][index].fg(parser.colors().info),
|
||||
["$|", "$"][index].fg(parser.colors().info)
|
||||
["|$", "$"][index].fg(state.parser.colors().info),
|
||||
["$|", "$"][index].fg(state.parser.colors().info)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -344,7 +342,7 @@ impl RegexRule for TexRule {
|
|||
.with_label(
|
||||
Label::new((token.source().clone(), content.range()))
|
||||
.with_message("Tex code is empty")
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -354,7 +352,8 @@ impl RegexRule for TexRule {
|
|||
};
|
||||
|
||||
// Properties
|
||||
let properties = match self.parse_properties(parser.colors(), &token, &matches.get(1)) {
|
||||
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(1))
|
||||
{
|
||||
Ok(pm) => pm,
|
||||
Err(report) => {
|
||||
reports.push(report);
|
||||
|
@ -376,10 +375,10 @@ impl RegexRule for TexRule {
|
|||
Label::new((token.source().clone(), token.range.clone()))
|
||||
.with_message(format!(
|
||||
"Property `kind: {}` cannot be converted: {}",
|
||||
prop.fg(parser.colors().info),
|
||||
err.fg(parser.colors().error)
|
||||
prop.fg(state.parser.colors().info),
|
||||
err.fg(state.parser.colors().error)
|
||||
))
|
||||
.with_color(parser.colors().warning),
|
||||
.with_color(state.parser.colors().warning),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -412,7 +411,7 @@ impl RegexRule for TexRule {
|
|||
.and_then(|(_, value)| Some(value))
|
||||
.unwrap();
|
||||
|
||||
parser.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Tex {
|
||||
mathmode: index == 1,
|
||||
|
@ -426,15 +425,13 @@ impl RegexRule for TexRule {
|
|||
|
||||
reports
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::parser::langparser::LangParser;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::validate_document;
|
||||
|
||||
|
@ -453,7 +450,7 @@ $[kind=block,env=another] e^{i\pi}=-1$
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) };
|
||||
|
@ -475,7 +472,7 @@ $[env=another] e^{i\pi}=-1$
|
|||
None,
|
||||
));
|
||||
let parser = LangParser::default();
|
||||
let doc = parser.parse(source, None);
|
||||
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||
|
||||
validate_document!(doc.content().borrow(), 0,
|
||||
Paragraph {
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::document::document::Document;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::rule::Rule;
|
||||
use crate::parser::source::Cursor;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -48,26 +48,26 @@ pub struct TextRule;
|
|||
impl Rule for TextRule {
|
||||
fn name(&self) -> &'static str { "Text" }
|
||||
|
||||
fn next_match(&self, _parser: &dyn Parser, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None }
|
||||
fn next_match(&self, _state: &ParserState, _cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> { None }
|
||||
|
||||
fn on_match(
|
||||
&self,
|
||||
_parser: &dyn Parser,
|
||||
_state: &ParserState,
|
||||
_document: &dyn Document,
|
||||
_cursor: Cursor,
|
||||
_match_data: Option<Box<dyn Any>>,
|
||||
_match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
panic!("Text cannot match");
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
bindings.push((
|
||||
"push".to_string(),
|
||||
lua.create_function(|_, content: String| {
|
||||
CTX.with_borrow(|ctx| {
|
||||
ctx.as_ref().map(|ctx| {
|
||||
ctx.parser.push(
|
||||
ctx.state.push(
|
||||
ctx.document,
|
||||
Box::new(Text {
|
||||
location: ctx.location.clone(),
|
||||
|
@ -82,6 +82,6 @@ impl Rule for TextRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::document::variable::BaseVariable;
|
|||
use crate::document::variable::PathVariable;
|
||||
use crate::document::variable::Variable;
|
||||
use crate::lua::kernel::CTX;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::parser::ReportColors;
|
||||
use crate::parser::rule::RegexRule;
|
||||
use crate::parser::source::Source;
|
||||
|
@ -123,7 +123,7 @@ impl RegexRule for VariableRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document,
|
||||
token: Token,
|
||||
matches: regex::Captures,
|
||||
|
@ -148,9 +148,9 @@ impl RegexRule for VariableRule {
|
|||
Label::new((token.source(), kind.range()))
|
||||
.with_message(format!(
|
||||
"Variable kind `{}` is unknown",
|
||||
kind.as_str().fg(parser.colors().highlight)
|
||||
kind.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_help(format!(
|
||||
"Leave empty for regular variables. Available variable kinds:{}",
|
||||
|
@ -159,8 +159,8 @@ impl RegexRule for VariableRule {
|
|||
|acc, (char, name)| {
|
||||
acc + format!(
|
||||
"\n - `{}` : {}",
|
||||
char.fg(parser.colors().highlight),
|
||||
name.fg(parser.colors().info)
|
||||
char.fg(state.parser.colors().highlight),
|
||||
name.fg(state.parser.colors().info)
|
||||
)
|
||||
.as_str()
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ impl RegexRule for VariableRule {
|
|||
};
|
||||
|
||||
let var_name = match matches.get(2) {
|
||||
Some(name) => match VariableRule::validate_name(&parser.colors(), name.as_str()) {
|
||||
Some(name) => match VariableRule::validate_name(&state.parser.colors(), name.as_str()) {
|
||||
Ok(var_name) => var_name,
|
||||
Err(msg) => {
|
||||
result.push(
|
||||
|
@ -188,9 +188,9 @@ impl RegexRule for VariableRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Variable name `{}` is not allowed. {msg}",
|
||||
name.as_str().fg(parser.colors().highlight)
|
||||
name.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -212,9 +212,9 @@ impl RegexRule for VariableRule {
|
|||
Label::new((token.source(), value.range()))
|
||||
.with_message(format!(
|
||||
"Variable value `{}` is not allowed. {msg}",
|
||||
value.as_str().fg(parser.colors().highlight)
|
||||
value.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -226,7 +226,7 @@ impl RegexRule for VariableRule {
|
|||
};
|
||||
|
||||
match self.make_variable(
|
||||
&parser.colors(),
|
||||
&state.parser.colors(),
|
||||
token.clone(),
|
||||
var_kind,
|
||||
var_name.to_string(),
|
||||
|
@ -242,10 +242,10 @@ impl RegexRule for VariableRule {
|
|||
Label::new((token.source(), m.start() + 1..m.end()))
|
||||
.with_message(format!(
|
||||
"Unable to create variable `{}`. {}",
|
||||
var_name.fg(parser.colors().highlight),
|
||||
var_name.fg(state.parser.colors().highlight),
|
||||
msg
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -257,7 +257,7 @@ impl RegexRule for VariableRule {
|
|||
return result;
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
let mut bindings = vec![];
|
||||
bindings.push((
|
||||
"insert".to_string(),
|
||||
|
@ -291,7 +291,7 @@ impl RegexRule for VariableRule {
|
|||
.unwrap(),
|
||||
));
|
||||
|
||||
Some(bindings)
|
||||
bindings
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +315,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
_index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a dyn Document<'a>,
|
||||
token: Token,
|
||||
matches: regex::Captures,
|
||||
|
@ -332,7 +332,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), matches.get(0).unwrap().range()))
|
||||
.with_message(format!("Missing variable name for substitution"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -347,7 +347,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!("Variable names contains leading spaces"))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_help("Remove leading spaces")
|
||||
.finish(),
|
||||
|
@ -365,7 +365,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
.with_message(format!(
|
||||
"Variable names contains trailing spaces"
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.with_help("Remove trailing spaces")
|
||||
.finish(),
|
||||
|
@ -374,7 +374,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
return result;
|
||||
}
|
||||
// Invalid name
|
||||
match VariableRule::validate_name(&parser.colors(), name.as_str()) {
|
||||
match VariableRule::validate_name(&state.parser.colors(), name.as_str()) {
|
||||
Err(msg) => {
|
||||
result.push(
|
||||
Report::build(ReportKind::Error, token.source(), name.start())
|
||||
|
@ -382,7 +382,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
.with_label(
|
||||
Label::new((token.source(), name.range()))
|
||||
.with_message(msg)
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -402,9 +402,9 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
Label::new((token.source(), name.range()))
|
||||
.with_message(format!(
|
||||
"Unable to find variable with name: `{}`",
|
||||
name.as_str().fg(parser.colors().highlight)
|
||||
name.as_str().fg(state.parser.colors().highlight)
|
||||
))
|
||||
.with_color(parser.colors().error),
|
||||
.with_color(state.parser.colors().error),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
|
@ -416,10 +416,8 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
_ => panic!("Unknown error"),
|
||||
};
|
||||
|
||||
variable.parse(token, parser, document);
|
||||
variable.parse(state, token, document);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
}
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
use std::cell::RefCell;
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::Error;
|
||||
use mlua::FromLuaMulti;
|
||||
use mlua::Function;
|
||||
use mlua::IntoLuaMulti;
|
||||
use mlua::Lua;
|
||||
|
||||
use crate::document::document::Document;
|
||||
use crate::parser::parser::Parser;
|
||||
use crate::parser::parser::ParserState;
|
||||
use crate::parser::source::Token;
|
||||
|
||||
pub struct KernelContext<'a, 'b> {
|
||||
pub struct KernelContext<'a, 'b, 'c> {
|
||||
pub location: Token,
|
||||
pub parser: &'a dyn Parser,
|
||||
pub document: &'b dyn Document<'b>,
|
||||
//pub parser: &'a dyn Parser,
|
||||
pub state: &'a ParserState<'a, 'b>,
|
||||
pub document: &'c dyn Document<'c>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static CTX: RefCell<Option<KernelContext<'static, 'static>>> = RefCell::new(None);
|
||||
pub static CTX: RefCell<Option<KernelContext<'static, 'static, 'static>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -28,9 +24,6 @@ pub struct 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 {
|
||||
let lua = Lua::new();
|
||||
|
||||
|
@ -38,17 +31,14 @@ impl Kernel {
|
|||
let nml_table = lua.create_table().unwrap();
|
||||
|
||||
for rule in parser.rules() {
|
||||
if let Some(bindings) = rule.lua_bindings(&lua) {
|
||||
let table = lua.create_table().unwrap();
|
||||
// TODO: Export this so we can check for duplicate rules based on this name
|
||||
let name = rule.name().to_lowercase().replace(' ', "_");
|
||||
|
||||
for (fun_name, fun) in bindings {
|
||||
for (fun_name, fun) in rule.register_bindings(&lua) {
|
||||
table.set(fun_name, fun).unwrap();
|
||||
}
|
||||
|
||||
nml_table.set(name, table).unwrap();
|
||||
}
|
||||
}
|
||||
lua.globals().set("nml", nml_table).unwrap();
|
||||
}
|
||||
|
||||
|
@ -71,8 +61,15 @@ impl Kernel {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait KernelHolder {
|
||||
fn get_kernel(&self, name: &str) -> Option<RefMut<'_, Kernel>>;
|
||||
|
||||
fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>;
|
||||
#[derive(Default)]
|
||||
pub struct KernelHolder {
|
||||
kernels: HashMap<String, Kernel>,
|
||||
}
|
||||
|
||||
impl KernelHolder {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -21,6 +21,7 @@ use document::document::Document;
|
|||
use getopts::Options;
|
||||
use parser::langparser::LangParser;
|
||||
use parser::parser::Parser;
|
||||
use parser::parser::ParserState;
|
||||
use rusqlite::Connection;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
|
@ -46,13 +47,16 @@ NML version: 0.4\n"
|
|||
);
|
||||
}
|
||||
|
||||
fn parse(input: &str, debug_opts: &Vec<String>) -> Result<Box<dyn Document<'static>>, String> {
|
||||
fn parse(
|
||||
parser: &LangParser,
|
||||
input: &str,
|
||||
debug_opts: &Vec<String>,
|
||||
) -> Result<Box<dyn Document<'static>>, String> {
|
||||
println!("Parsing {input}...");
|
||||
let parser = LangParser::default();
|
||||
|
||||
// Parse
|
||||
let source = SourceFile::new(input.to_string(), None).unwrap();
|
||||
let doc = parser.parse(Rc::new(source), None);
|
||||
let (doc, _) = parser.parse(ParserState::new(parser, None), Rc::new(source), None);
|
||||
|
||||
if debug_opts.contains(&"ast".to_string()) {
|
||||
println!("-- BEGIN AST DEBUGGING --");
|
||||
|
@ -106,6 +110,7 @@ fn process(
|
|||
CompiledDocument::init_cache(&con)
|
||||
.map_err(|err| format!("Failed to initialize cached document table: {err}"))?;
|
||||
|
||||
let parser = LangParser::default();
|
||||
for file in files {
|
||||
let meta = std::fs::metadata(&file)
|
||||
.map_err(|err| format!("Failed to get metadata for `{file:#?}`: {err}"))?;
|
||||
|
@ -123,7 +128,7 @@ fn process(
|
|||
|
||||
let parse_and_compile = || -> Result<CompiledDocument, String> {
|
||||
// Parse
|
||||
let doc = parse(file.to_str().unwrap(), debug_opts)?;
|
||||
let doc = parse(&parser, file.to_str().unwrap(), debug_opts)?;
|
||||
|
||||
// Compile
|
||||
let compiler = Compiler::new(target, db_path.clone());
|
||||
|
@ -216,8 +221,7 @@ fn main() -> ExitCode {
|
|||
}
|
||||
match std::fs::metadata(&output) {
|
||||
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");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
|
63
src/parser/customstyle.rs
Normal file
63
src/parser/customstyle.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,44 +1,19 @@
|
|||
use std::any::Any;
|
||||
use std::cell::Ref;
|
||||
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 ariadne::Label;
|
||||
use ariadne::Report;
|
||||
|
||||
use crate::document::customstyle::CustomStyle;
|
||||
use crate::document::customstyle::CustomStyleHolder;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::document::DocumentAccessors;
|
||||
use crate::document::element::ContainerElement;
|
||||
use crate::document::element::DocumentEnd;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
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::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::ParserStrategy;
|
||||
use super::parser::ParserState;
|
||||
use super::parser::ReportColors;
|
||||
use super::rule::Rule;
|
||||
use super::source::Cursor;
|
||||
use super::source::Source;
|
||||
use super::source::Token;
|
||||
use super::state::StateHolder;
|
||||
use super::util;
|
||||
|
||||
/// Parser for the language
|
||||
|
@ -49,13 +24,6 @@ pub struct LangParser {
|
|||
|
||||
// Parser state
|
||||
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 {
|
||||
|
@ -64,90 +32,14 @@ impl LangParser {
|
|||
rules: vec![],
|
||||
colors: ReportColors::with_colors(),
|
||||
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
|
||||
// TODO: use https://docs.rs/inventory/latest/inventory/
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -156,50 +48,15 @@ impl Parser for LangParser {
|
|||
fn rules(&self) -> &Vec<Box<dyn Rule>> { &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() }
|
||||
|
||||
/// Add an [`Element`] to the [`Document`]
|
||||
fn push<'a>(&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.handle_reports(
|
||||
doc.source(),
|
||||
self.state_mut()
|
||||
.on_scope_end(self, doc, super::state::Scope::PARAGRAPH),
|
||||
);
|
||||
}
|
||||
|
||||
doc.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse<'a>(
|
||||
&self,
|
||||
fn parse<'p, 'a, 'doc>(
|
||||
&'p self,
|
||||
state: ParserState<'p, 'a>,
|
||||
source: Rc<dyn Source>,
|
||||
parent: Option<&'a dyn Document<'a>>,
|
||||
) -> Box<dyn Document<'a> + 'a> {
|
||||
parent: Option<&'doc dyn Document<'doc>>,
|
||||
) -> (Box<dyn Document<'doc> + 'doc>, ParserState<'p, 'a>) {
|
||||
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 mut cursor = Cursor::new(0usize, doc.source()); // Cursor in file
|
||||
|
@ -207,21 +64,21 @@ impl Parser for LangParser {
|
|||
if let Some(parent) = parent
|
||||
// Terminate parent's paragraph state
|
||||
{
|
||||
self.handle_reports(
|
||||
parent.source(),
|
||||
self.state_mut()
|
||||
.on_scope_end(self, parent, super::state::Scope::PARAGRAPH),
|
||||
);
|
||||
self.handle_reports(state.shared.rule_state.borrow_mut().on_scope_end(
|
||||
&state,
|
||||
parent,
|
||||
super::state::Scope::PARAGRAPH,
|
||||
));
|
||||
}
|
||||
|
||||
loop {
|
||||
let (rule_pos, rule, match_data) = self.update_matches(&cursor, &mut matches);
|
||||
let (rule_pos, mut result) = state.update_matches(&cursor);
|
||||
|
||||
// Unmatched content
|
||||
let text_content =
|
||||
util::process_text(&doc, &content.as_str()[cursor.pos..rule_pos.pos]);
|
||||
if !text_content.is_empty() {
|
||||
self.push(
|
||||
state.push(
|
||||
&doc,
|
||||
Box::new(Text::new(
|
||||
Token::new(cursor.pos..rule_pos.pos, source.clone()),
|
||||
|
@ -230,12 +87,13 @@ impl Parser for LangParser {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(rule) = rule {
|
||||
if let Some((rule_index, match_data)) = result.take() {
|
||||
// Rule callback
|
||||
let dd: &'a dyn Document = unsafe { std::mem::transmute(&doc as &dyn Document) };
|
||||
let (new_cursor, reports) = rule.on_match(self, dd, rule_pos, match_data);
|
||||
let (new_cursor, reports) =
|
||||
self.rules[rule_index].on_match(&state, dd, rule_pos, match_data);
|
||||
|
||||
self.handle_reports(doc.source(), reports);
|
||||
self.handle_reports(reports);
|
||||
|
||||
// Advance
|
||||
cursor = new_cursor;
|
||||
|
@ -246,14 +104,14 @@ impl Parser for LangParser {
|
|||
}
|
||||
}
|
||||
|
||||
// State
|
||||
self.handle_reports(
|
||||
doc.source(),
|
||||
self.state_mut()
|
||||
.on_scope_end(self, &doc, super::state::Scope::DOCUMENT),
|
||||
);
|
||||
// Rule States
|
||||
self.handle_reports(state.shared.rule_state.borrow_mut().on_scope_end(
|
||||
&state,
|
||||
&doc,
|
||||
super::state::Scope::DOCUMENT,
|
||||
));
|
||||
|
||||
self.push(
|
||||
state.push(
|
||||
&doc,
|
||||
Box::new(DocumentEnd(Token::new(
|
||||
doc.source().content().len()..doc.source().content().len(),
|
||||
|
@ -261,26 +119,26 @@ impl Parser for LangParser {
|
|||
))),
|
||||
);
|
||||
|
||||
return Box::new(doc);
|
||||
}
|
||||
|
||||
fn parse_into<'a>(&self, source: Rc<dyn Source>, document: &'a dyn Document<'a>) {
|
||||
let mut matches = Vec::new();
|
||||
for _ in 0..self.rules.len() {
|
||||
matches.push((0usize, None));
|
||||
return (Box::new(doc), state);
|
||||
}
|
||||
|
||||
fn parse_into<'p, 'a, 'doc>(
|
||||
&'p self,
|
||||
state: ParserState<'p, 'a>,
|
||||
source: Rc<dyn Source>,
|
||||
document: &'doc dyn Document<'doc>,
|
||||
) -> ParserState<'p, 'a> {
|
||||
let content = source.content();
|
||||
let mut cursor = Cursor::new(0usize, source.clone());
|
||||
|
||||
loop {
|
||||
let (rule_pos, rule, match_data) = self.update_matches(&cursor, &mut matches);
|
||||
let (rule_pos, mut result) = state.update_matches(&cursor);
|
||||
|
||||
// Unmatched content
|
||||
let text_content =
|
||||
util::process_text(document, &content.as_str()[cursor.pos..rule_pos.pos]);
|
||||
if !text_content.is_empty() {
|
||||
self.push(
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Text::new(
|
||||
Token::new(cursor.pos..rule_pos.pos, source.clone()),
|
||||
|
@ -289,11 +147,12 @@ impl Parser for LangParser {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(rule) = rule {
|
||||
if let Some((rule_index, match_data)) = result.take() {
|
||||
// Rule callback
|
||||
let (new_cursor, reports) = (*rule).on_match(self, document, rule_pos, match_data);
|
||||
let (new_cursor, reports) =
|
||||
self.rules[rule_index].on_match(&state, document, rule_pos, match_data);
|
||||
|
||||
self.handle_reports(document.source(), reports);
|
||||
self.handle_reports(reports);
|
||||
|
||||
// Advance
|
||||
cursor = new_cursor;
|
||||
|
@ -304,6 +163,7 @@ impl Parser for LangParser {
|
|||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
// State
|
||||
//self.handle_reports(source.clone(),
|
||||
// self.state_mut().on_scope_end(&self, &document, super::state::Scope::DOCUMENT));
|
||||
|
@ -311,43 +171,3 @@ impl Parser for LangParser {
|
|||
//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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
use std::any::Any;
|
||||
use std::cell::Ref;
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::compiler::compiler::Compiler;
|
||||
use crate::document::document::Document;
|
||||
use crate::elements::layout::LayoutToken;
|
||||
|
||||
use super::document::Document;
|
||||
|
||||
/// Represents the type of a layout
|
||||
pub trait LayoutType: core::fmt::Debug {
|
||||
/// Name of the layout
|
||||
|
@ -32,18 +29,17 @@ pub trait LayoutType: core::fmt::Debug {
|
|||
) -> Result<String, String>;
|
||||
}
|
||||
|
||||
pub trait LayoutHolder {
|
||||
/// gets a reference to all defined layouts
|
||||
fn layouts(&self) -> Ref<'_, HashMap<String, Rc<dyn LayoutType>>>;
|
||||
#[derive(Default)]
|
||||
pub struct LayoutHolder {
|
||||
layouts: HashMap<String, Rc<dyn LayoutType>>,
|
||||
}
|
||||
|
||||
/// gets a (mutable) reference to all defined layours
|
||||
fn layouts_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn LayoutType>>>;
|
||||
|
||||
fn get_layout(&self, layout_name: &str) -> Option<Rc<dyn LayoutType>> {
|
||||
self.layouts().get(layout_name).map(|layout| layout.clone())
|
||||
impl LayoutHolder {
|
||||
pub fn get(&self, layout_name: &str) -> Option<Rc<dyn LayoutType>> {
|
||||
self.layouts.get(layout_name).map(|layout| layout.clone())
|
||||
}
|
||||
|
||||
fn insert_layout(&self, layout: Rc<dyn LayoutType>) {
|
||||
self.layouts_mut().insert(layout.name().into(), layout);
|
||||
pub fn insert(&mut self, layout: Rc<dyn LayoutType>) {
|
||||
self.layouts.insert(layout.name().into(), layout);
|
||||
}
|
||||
}
|
|
@ -4,3 +4,6 @@ pub mod rule;
|
|||
pub mod source;
|
||||
pub mod state;
|
||||
pub mod util;
|
||||
pub mod style;
|
||||
pub mod layout;
|
||||
pub mod customstyle;
|
||||
|
|
|
@ -1,20 +1,30 @@
|
|||
use ariadne::Label;
|
||||
use ariadne::Report;
|
||||
use std::any::Any;
|
||||
use std::cell::Ref;
|
||||
use std::cell::RefMut;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::customstyle::CustomStyleHolder;
|
||||
use super::layout::LayoutHolder;
|
||||
use super::rule::Rule;
|
||||
use super::source::Cursor;
|
||||
use super::source::Source;
|
||||
use super::state::StateHolder;
|
||||
use crate::document::customstyle::CustomStyleHolder;
|
||||
use super::state::RuleStateHolder;
|
||||
use super::style::StyleHolder;
|
||||
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::layout::LayoutHolder;
|
||||
use crate::document::style::StyleHolder;
|
||||
use crate::elements::customstyle::CustomStyleRule;
|
||||
use crate::elements::paragraph::Paragraph;
|
||||
use crate::lua::kernel::Kernel;
|
||||
use crate::lua::kernel::KernelHolder;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::parser::source::VirtualSource;
|
||||
use ariadne::Color;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -45,94 +55,137 @@ impl ReportColors {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Parser: KernelHolder + StyleHolder + LayoutHolder + CustomStyleHolder {
|
||||
/// Gets the colors for formatting errors
|
||||
/// The state that is shared with the state's childre
|
||||
pub struct SharedState {
|
||||
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
|
||||
///
|
||||
/// When colors are disabled, all colors should resolve to empty string
|
||||
fn colors(&self) -> &ReportColors;
|
||||
/// Parent should be None when parsing a brand new document. If you have to
|
||||
/// set the parent to Some(..) (e.g for imports or sub-document), be sure
|
||||
/// 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))
|
||||
};
|
||||
|
||||
/// 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>>;
|
||||
|
||||
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}`"
|
||||
));
|
||||
Self {
|
||||
parser,
|
||||
parent,
|
||||
matches: RefCell::new(matches),
|
||||
shared,
|
||||
}
|
||||
}
|
||||
|
||||
match after {
|
||||
Some(name) => {
|
||||
let before = self
|
||||
/// Runs a procedure with a new state that inherits the [`SharedState`] state from [`self`]
|
||||
///
|
||||
/// Note: When parsing a new document, create a new state, then the parsing process
|
||||
/// creates states using this method
|
||||
pub fn with_state<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(ParserState) -> R,
|
||||
{
|
||||
let new_state = ParserState::new(self.parser, Some(self));
|
||||
f(new_state)
|
||||
}
|
||||
|
||||
/// Updates matches from a given start position e.g [`Cursor`]
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// 1. The cursor position after updating the matches
|
||||
/// 2. (Optional) The winning match with it's match data
|
||||
/// 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.
|
||||
///
|
||||
/// # 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
|
||||
.rules()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_pos, r)| (r).name() == name);
|
||||
|
||||
match before {
|
||||
Some((pos, _)) => self.rules_mut().insert(pos + 1, rule),
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Unable to find rule named `{name}`, to insert rule `{}` after it",
|
||||
rule.name()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => self.rules_mut().push(rule),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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()
|
||||
.zip(matches.iter_mut())
|
||||
.zip(matches_borrow.iter_mut())
|
||||
.for_each(|(rule, (matched_at, match_data))| {
|
||||
// Don't upate if not stepped over yet
|
||||
if *matched_at > cursor.pos && rule.downcast_ref::<CustomStyleRule>().is_none() {
|
||||
if *matched_at > cursor.pos {
|
||||
// TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule
|
||||
return;
|
||||
}
|
||||
|
@ -170,23 +223,237 @@ impl<T: Parser> ParserStrategy for T {
|
|||
});
|
||||
|
||||
// Get winning match
|
||||
let (winner, (next_pos, _match_data)) = matches
|
||||
let (winner, next_pos) = matches_borrow
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|(_, (pos, _match_data))| pos)
|
||||
.min_by_key(|(_, (pos, _))| pos)
|
||||
.map(|(winner, (pos, _))| (winner, *pos))
|
||||
.unwrap();
|
||||
if *next_pos == usize::MAX
|
||||
|
||||
if next_pos == usize::MAX
|
||||
// No rule has matched
|
||||
{
|
||||
let content = cursor.source.content();
|
||||
// No winners, i.e no matches left
|
||||
return (cursor.at(content.len()), None, None);
|
||||
return (cursor.at(content.len()), None);
|
||||
}
|
||||
|
||||
(
|
||||
cursor.at(*next_pos),
|
||||
Some(&self.rules()[winner]),
|
||||
std::mem::replace(&mut matches[winner].1, None),
|
||||
)
|
||||
return (
|
||||
cursor.at(next_pos),
|
||||
Some((winner, matches_borrow[winner].1.take().unwrap())),
|
||||
);
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use super::parser::Parser;
|
||||
use super::layout::LayoutHolder;
|
||||
use super::parser::ParserState;
|
||||
use super::source::Cursor;
|
||||
use super::source::Source;
|
||||
use super::source::Token;
|
||||
use super::style::StyleHolder;
|
||||
use crate::document::document::Document;
|
||||
use ariadne::Report;
|
||||
use downcast_rs::impl_downcast;
|
||||
|
@ -17,23 +19,24 @@ pub trait Rule: Downcast {
|
|||
/// Returns rule's name
|
||||
fn name(&self) -> &'static str;
|
||||
/// Finds the next match starting from [`cursor`]
|
||||
fn next_match(&self, parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)>;
|
||||
fn next_match(&self, state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)>;
|
||||
/// Callback when rule matches
|
||||
fn on_match<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
cursor: Cursor,
|
||||
match_data: Option<Box<dyn Any>>,
|
||||
match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>);
|
||||
/// Export bindings to lua
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
|
||||
/// Registers lua bindings
|
||||
fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
||||
|
||||
/// Registers default styles
|
||||
fn register_styles(&self, _parser: &dyn Parser) {}
|
||||
fn register_styles(&self, _holder: &mut StyleHolder) {}
|
||||
|
||||
/// Registers default layouts
|
||||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||
fn register_layouts(&self, _holder: &mut LayoutHolder) {}
|
||||
}
|
||||
impl_downcast!(Rule);
|
||||
|
||||
|
@ -53,24 +56,22 @@ pub trait RegexRule {
|
|||
fn on_regex_match<'a>(
|
||||
&self,
|
||||
index: usize,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
token: Token,
|
||||
matches: regex::Captures,
|
||||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>;
|
||||
|
||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||
fn register_styles(&self, _parser: &dyn Parser) {}
|
||||
fn register_layouts(&self, _parser: &dyn Parser) {}
|
||||
fn register_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
||||
fn register_styles(&self, _holder: &mut StyleHolder) {}
|
||||
fn register_layouts(&self, _holder: &mut LayoutHolder) {}
|
||||
}
|
||||
|
||||
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`]
|
||||
fn next_match(&self, _parser: &dyn Parser, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
fn next_match(&self, _state: &ParserState, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
|
||||
let content = cursor.source.content();
|
||||
let mut found: Option<(usize, usize)> = None;
|
||||
self.regexes().iter().enumerate().for_each(|(id, re)| {
|
||||
|
@ -92,18 +93,13 @@ impl<T: RegexRule + 'static> Rule for T {
|
|||
|
||||
fn on_match<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &'a (dyn Document<'a> + 'a),
|
||||
cursor: Cursor,
|
||||
match_data: Option<Box<dyn Any>>,
|
||||
match_data: Box<dyn Any>,
|
||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
|
||||
let content = cursor.source.content();
|
||||
let index = unsafe {
|
||||
match_data
|
||||
.unwrap_unchecked()
|
||||
.downcast::<usize>()
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
let index = match_data.downcast::<usize>().unwrap();
|
||||
let re = &self.regexes()[*index];
|
||||
|
||||
let captures = re.captures_at(content.as_str(), cursor.pos).unwrap();
|
||||
|
@ -112,19 +108,15 @@ impl<T: RegexRule + 'static> Rule for T {
|
|||
let token_end = token.end();
|
||||
return (
|
||||
cursor.at(token_end),
|
||||
self.on_regex_match(*index, parser, document, token, captures),
|
||||
self.on_regex_match(*index, state, document, token, captures),
|
||||
);
|
||||
}
|
||||
|
||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||
self.lua_bindings(lua)
|
||||
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||
self.register_bindings(lua)
|
||||
}
|
||||
|
||||
fn register_styles(&self, parser: &dyn Parser) {
|
||||
self.register_styles(parser);
|
||||
}
|
||||
fn register_styles(&self, holder: &mut StyleHolder) { self.register_styles(holder); }
|
||||
|
||||
fn register_layouts(&self, parser: &dyn Parser) {
|
||||
self.register_layouts(parser);
|
||||
}
|
||||
fn register_layouts(&self, holder: &mut LayoutHolder) { self.register_layouts(holder); }
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use downcast_rs::Downcast;
|
|||
|
||||
use crate::document::document::Document;
|
||||
|
||||
use super::parser::Parser;
|
||||
use super::parser::ParserState;
|
||||
use super::source::Source;
|
||||
|
||||
/// Scope for state objects
|
||||
|
@ -25,75 +25,69 @@ pub enum Scope {
|
|||
PARAGRAPH = 2,
|
||||
}
|
||||
|
||||
pub trait State: Downcast {
|
||||
pub trait RuleState: Downcast {
|
||||
/// Returns the state's [`Scope`]
|
||||
fn scope(&self) -> Scope;
|
||||
|
||||
/// Callback called when state goes out of scope
|
||||
fn on_remove<'a>(
|
||||
&self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
) -> Vec<Report<'a, (Rc<dyn Source>, Range<usize>)>>;
|
||||
}
|
||||
impl_downcast!(State);
|
||||
impl_downcast!(RuleState);
|
||||
|
||||
impl core::fmt::Debug for dyn State {
|
||||
impl core::fmt::Debug for dyn RuleState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "State{{Scope: {:#?}}}", self.scope())
|
||||
}
|
||||
}
|
||||
|
||||
/// Object owning all the states
|
||||
#[derive(Debug)]
|
||||
pub struct StateHolder {
|
||||
data: HashMap<String, Rc<RefCell<dyn State>>>,
|
||||
#[derive(Default)]
|
||||
pub struct RuleStateHolder {
|
||||
states: HashMap<String, Rc<RefCell<dyn RuleState>>>,
|
||||
}
|
||||
|
||||
impl StateHolder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to push [`state`]. On collision, returns an error with the already present state
|
||||
impl RuleStateHolder {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
name: String,
|
||||
state: Rc<RefCell<dyn State>>,
|
||||
) -> Result<Rc<RefCell<dyn State>>, Rc<RefCell<dyn State>>> {
|
||||
match self.data.insert(name, state.clone()) {
|
||||
Some(state) => Err(state),
|
||||
_ => Ok(state),
|
||||
state: Rc<RefCell<dyn RuleState>>,
|
||||
) -> Result<Rc<RefCell<dyn RuleState>>, String> {
|
||||
if self.states.contains_key(name.as_str()) {
|
||||
return Err(format!("Attempted to insert duplicate RuleState: {name}"));
|
||||
}
|
||||
self.states.insert(name, state.clone());
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn query(&self, name: &String) -> Option<Rc<RefCell<dyn State>>> {
|
||||
self.data.get(name).map_or(None, |st| Some(st.clone()))
|
||||
pub fn get(&self, state_name: &str) -> Option<Rc<RefCell<dyn RuleState>>> {
|
||||
self.states.get(state_name).map(|state| state.clone())
|
||||
}
|
||||
|
||||
pub fn on_scope_end(
|
||||
&mut self,
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
scope: Scope,
|
||||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut result = vec![];
|
||||
let mut reports = vec![];
|
||||
|
||||
self.data.retain(|_name, state| {
|
||||
if state.borrow().scope() >= scope {
|
||||
state
|
||||
.borrow()
|
||||
.on_remove(parser, document)
|
||||
self.states.retain(|_name, rule_state| {
|
||||
if rule_state.borrow().scope() >= scope {
|
||||
rule_state
|
||||
.borrow_mut()
|
||||
.on_remove(state, document)
|
||||
.drain(..)
|
||||
.for_each(|report| result.push(report));
|
||||
.for_each(|report| reports.push(report));
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
return reports;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::cell::Ref;
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -27,48 +25,53 @@ pub trait ElementStyle: Downcast + core::fmt::Debug {
|
|||
}
|
||||
impl_downcast!(ElementStyle);
|
||||
|
||||
pub trait StyleHolder {
|
||||
/// gets a reference to all defined styles
|
||||
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>>>;
|
||||
#[derive(Default)]
|
||||
pub struct StyleHolder {
|
||||
styles: HashMap<String, Rc<dyn ElementStyle>>,
|
||||
}
|
||||
|
||||
impl StyleHolder {
|
||||
/// Checks if a given style key is registered
|
||||
fn is_style_registered(&self, style_key: &str) -> bool { self.element_styles().contains_key(style_key) }
|
||||
pub fn is_registered(&self, style_key: &str) -> bool { self.styles.contains_key(style_key) }
|
||||
|
||||
/// Gets the current active style for an element
|
||||
/// NOTE: Will panic if a style is not defined for a given element
|
||||
/// If you need to process user input, use [`is_registered`]
|
||||
fn current_style(&self, style_key: &str) -> Rc<dyn ElementStyle> {
|
||||
self.element_styles().get(style_key).map(|rc| rc.clone()).unwrap()
|
||||
pub fn current(&self, style_key: &str) -> Rc<dyn ElementStyle> {
|
||||
self.styles.get(style_key).map(|rc| rc.clone()).unwrap()
|
||||
}
|
||||
|
||||
/// Sets the [`style`]
|
||||
fn set_current_style(&self, style: Rc<dyn ElementStyle>) {
|
||||
self.element_styles_mut().insert(style.key().to_string(), style);
|
||||
pub fn set_current(&mut self, style: Rc<dyn ElementStyle>) {
|
||||
self.styles.insert(style.key().to_string(), style);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_elementstyle {
|
||||
($t:ty, $key:expr) => {
|
||||
impl ElementStyle for $t {
|
||||
impl crate::parser::style::ElementStyle for $t {
|
||||
fn key(&self) -> &'static str { $key }
|
||||
|
||||
fn from_json(&self, json: &str) -> Result<std::rc::Rc<dyn ElementStyle>, String> {
|
||||
fn from_json(
|
||||
&self,
|
||||
json: &str,
|
||||
) -> Result<std::rc::Rc<dyn crate::parser::style::ElementStyle>, String> {
|
||||
serde_json::from_str::<$t>(json)
|
||||
.map_err(|e| e.to_string())
|
||||
.map(|obj| std::rc::Rc::new(obj) as std::rc::Rc<dyn ElementStyle>)
|
||||
.map(|obj| {
|
||||
std::rc::Rc::new(obj) as std::rc::Rc<dyn crate::parser::style::ElementStyle>
|
||||
})
|
||||
}
|
||||
|
||||
fn from_lua(
|
||||
&self,
|
||||
lua: &mlua::Lua,
|
||||
value: mlua::Value,
|
||||
) -> Result<std::rc::Rc<dyn ElementStyle>, mlua::Error> {
|
||||
mlua::LuaSerdeExt::from_value::<$t>(lua, value)
|
||||
.map(|obj| std::rc::Rc::new(obj) as std::rc::Rc<dyn ElementStyle>)
|
||||
) -> Result<std::rc::Rc<dyn crate::parser::style::ElementStyle>, mlua::Error> {
|
||||
mlua::LuaSerdeExt::from_value::<$t>(lua, value).map(|obj| {
|
||||
std::rc::Rc::new(obj) as std::rc::Rc<dyn crate::parser::style::ElementStyle>
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
|
@ -8,7 +8,7 @@ use crate::document::document::DocumentAccessors;
|
|||
use crate::document::element::ElemKind;
|
||||
use crate::elements::paragraph::Paragraph;
|
||||
|
||||
use super::parser::Parser;
|
||||
use super::parser::ParserState;
|
||||
use super::source::Source;
|
||||
|
||||
/// Processes text for escape characters and paragraphing
|
||||
|
@ -136,18 +136,24 @@ pub fn process_escaped<S: AsRef<str>>(escape: char, token: &'static str, content
|
|||
/// Parses source into a single paragraph
|
||||
/// If source contains anything but a single paragraph, an error is returned
|
||||
pub fn parse_paragraph<'a>(
|
||||
parser: &dyn Parser,
|
||||
state: &ParserState,
|
||||
source: Rc<dyn Source>,
|
||||
document: &'a dyn Document<'a>,
|
||||
) -> Result<Box<Paragraph>, &'static str> {
|
||||
let parsed = parser.parse(source.clone(), Some(document));
|
||||
let parsed = state.with_state(|new_state| -> Box<dyn Document> {
|
||||
new_state
|
||||
.parser
|
||||
.parse(new_state, source.clone(), Some(document))
|
||||
.0
|
||||
});
|
||||
if parsed.content().borrow().len() > 1 {
|
||||
return Err("Parsed document contains more than a single paragraph");
|
||||
} else if parsed.content().borrow().len() == 0 {
|
||||
return Err("Parsed document is empty");
|
||||
} else if parsed.last_element::<Paragraph>().is_none() {
|
||||
return Err("Parsed element is not a paragraph");
|
||||
} else if parser.has_error() {
|
||||
} else if state.parser.has_error() {
|
||||
// FIXME: If parser had an error before, this wold trigger
|
||||
return Err("Parser error");
|
||||
}
|
||||
|
||||
|
@ -417,20 +423,23 @@ mod tests {
|
|||
(&doc as &dyn Document)
|
||||
.last_element_mut::<Paragraph>()
|
||||
.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");
|
||||
|
||||
// A space is appended as previous element is inline
|
||||
(&doc as &dyn Document)
|
||||
.last_element_mut::<Paragraph>()
|
||||
.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");
|
||||
|
||||
(&doc as &dyn Document)
|
||||
.last_element_mut::<Paragraph>()
|
||||
.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");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue