2024-07-24 13:20:29 +02:00
|
|
|
use crate::compiler::compiler::Compiler;
|
|
|
|
use crate::document::document::Document;
|
|
|
|
use crate::document::element::ElemKind;
|
|
|
|
use crate::document::element::Element;
|
|
|
|
use crate::lua::kernel::CTX;
|
|
|
|
use crate::parser::parser::Parser;
|
|
|
|
use crate::parser::rule::RegexRule;
|
|
|
|
use crate::parser::source::Source;
|
|
|
|
use crate::parser::source::Token;
|
|
|
|
use crate::parser::util::Property;
|
|
|
|
use crate::parser::util::PropertyMapError;
|
|
|
|
use crate::parser::util::PropertyParser;
|
|
|
|
use crate::parser::util::{self};
|
|
|
|
use ariadne::Fmt;
|
|
|
|
use ariadne::Label;
|
|
|
|
use ariadne::Report;
|
|
|
|
use ariadne::ReportKind;
|
|
|
|
use mlua::Error::BadArgument;
|
|
|
|
use mlua::Function;
|
|
|
|
use mlua::Lua;
|
|
|
|
use regex::Captures;
|
|
|
|
use regex::Regex;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::ops::Range;
|
|
|
|
use std::rc::Rc;
|
|
|
|
use std::str::FromStr;
|
|
|
|
use std::sync::Arc;
|
2024-07-19 11:52:12 +02:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Raw {
|
2024-07-23 14:04:57 +02:00
|
|
|
pub(self) location: Token,
|
|
|
|
pub(self) kind: ElemKind,
|
|
|
|
pub(self) content: String,
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Raw {
|
2024-07-24 13:20:29 +02:00
|
|
|
fn new(location: Token, kind: ElemKind, content: String) -> Self {
|
|
|
|
Self {
|
|
|
|
location,
|
|
|
|
kind,
|
|
|
|
content,
|
|
|
|
}
|
|
|
|
}
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Element for Raw {
|
2024-07-24 13:20:29 +02:00
|
|
|
fn location(&self) -> &Token { &self.location }
|
|
|
|
fn kind(&self) -> ElemKind { self.kind.clone() }
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
fn element_name(&self) -> &'static str { "Raw" }
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
fn to_string(&self) -> String { format!("{self:#?}") }
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
2024-07-19 11:52:12 +02:00
|
|
|
Ok(self.content.clone())
|
2024-07-24 13:20:29 +02:00
|
|
|
}
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RawRule {
|
|
|
|
re: [Regex; 1],
|
|
|
|
properties: PropertyParser,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RawRule {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
let mut props = HashMap::new();
|
2024-07-24 13:20:29 +02:00
|
|
|
props.insert(
|
|
|
|
"kind".to_string(),
|
2024-07-19 11:52:12 +02:00
|
|
|
Property::new(
|
|
|
|
true,
|
|
|
|
"Element display kind".to_string(),
|
2024-07-24 13:20:29 +02:00
|
|
|
Some("inline".to_string()),
|
|
|
|
),
|
|
|
|
);
|
2024-07-19 11:52:12 +02:00
|
|
|
Self {
|
2024-07-24 13:20:29 +02:00
|
|
|
re: [
|
|
|
|
Regex::new(r"\{\?(?:\[((?:\\.|[^\[\]\\])*?)\])?(?:((?:\\.|[^\\\\])*?)(\?\}))?")
|
|
|
|
.unwrap(),
|
2024-07-19 11:52:12 +02:00
|
|
|
],
|
2024-07-24 13:20:29 +02:00
|
|
|
properties: PropertyParser::new(props),
|
|
|
|
}
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
impl RegexRule for RawRule {
|
|
|
|
fn name(&self) -> &'static str { "Raw" }
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
fn on_regex_match(
|
|
|
|
&self,
|
|
|
|
_index: usize,
|
|
|
|
parser: &dyn Parser,
|
|
|
|
document: &dyn Document,
|
|
|
|
token: Token,
|
|
|
|
matches: Captures,
|
|
|
|
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
2024-07-19 11:52:12 +02:00
|
|
|
let mut reports = vec![];
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
let raw_content = match matches.get(2) {
|
2024-07-19 11:52:12 +02:00
|
|
|
// Unterminated
|
|
|
|
None => {
|
|
|
|
reports.push(
|
|
|
|
Report::build(ReportKind::Error, token.source(), token.start())
|
2024-07-24 13:20:29 +02:00
|
|
|
.with_message("Unterminated Raw Code")
|
|
|
|
.with_label(
|
|
|
|
Label::new((token.source().clone(), token.range.clone()))
|
|
|
|
.with_message(format!(
|
|
|
|
"Missing terminating `{}` after first `{}`",
|
|
|
|
"?}".fg(parser.colors().info),
|
|
|
|
"{?".fg(parser.colors().info)
|
|
|
|
))
|
|
|
|
.with_color(parser.colors().error),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
|
|
|
return reports;
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
Some(content) => {
|
2024-07-24 13:20:29 +02:00
|
|
|
let processed =
|
|
|
|
util::process_escaped('\\', "?}", content.as_str().trim_start().trim_end());
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
if processed.is_empty() {
|
2024-07-19 11:52:12 +02:00
|
|
|
reports.push(
|
|
|
|
Report::build(ReportKind::Warning, token.source(), content.start())
|
2024-07-24 13:20:29 +02:00
|
|
|
.with_message("Empty Raw Code")
|
|
|
|
.with_label(
|
|
|
|
Label::new((token.source().clone(), content.range()))
|
|
|
|
.with_message("Raw code is empty")
|
|
|
|
.with_color(parser.colors().warning),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
processed
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
let properties = match matches.get(1) {
|
2024-07-19 11:52:12 +02:00
|
|
|
None => match self.properties.default() {
|
|
|
|
Ok(properties) => properties,
|
|
|
|
Err(e) => {
|
|
|
|
reports.push(
|
|
|
|
Report::build(ReportKind::Error, token.source(), token.start())
|
2024-07-24 13:20:29 +02:00
|
|
|
.with_message("Invalid Raw Code")
|
|
|
|
.with_label(
|
|
|
|
Label::new((token.source().clone(), token.range.clone()))
|
|
|
|
.with_message(format!("Raw code is missing properties: {e}"))
|
|
|
|
.with_color(parser.colors().error),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
|
|
|
return reports;
|
|
|
|
}
|
|
|
|
},
|
2024-07-19 11:52:12 +02:00
|
|
|
Some(props) => {
|
2024-07-24 13:20:29 +02:00
|
|
|
let processed =
|
|
|
|
util::process_escaped('\\', "]", props.as_str().trim_start().trim_end());
|
|
|
|
match self.properties.parse(processed.as_str()) {
|
2024-07-19 11:52:12 +02:00
|
|
|
Err(e) => {
|
|
|
|
reports.push(
|
|
|
|
Report::build(ReportKind::Error, token.source(), props.start())
|
2024-07-24 13:20:29 +02:00
|
|
|
.with_message("Invalid Raw Code Properties")
|
|
|
|
.with_label(
|
|
|
|
Label::new((token.source().clone(), props.range()))
|
|
|
|
.with_message(e)
|
|
|
|
.with_color(parser.colors().error),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
2024-07-19 11:52:12 +02:00
|
|
|
return reports;
|
|
|
|
}
|
2024-07-24 13:20:29 +02:00
|
|
|
Ok(properties) => properties,
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
let raw_kind: ElemKind = match properties.get("kind", |prop, value| {
|
|
|
|
ElemKind::from_str(value.as_str()).map_err(|e| (prop, e))
|
|
|
|
}) {
|
2024-07-19 11:52:12 +02:00
|
|
|
Ok((_prop, kind)) => kind,
|
2024-07-24 09:05:57 +02:00
|
|
|
Err(e) => match e {
|
|
|
|
PropertyMapError::ParseError((prop, err)) => {
|
|
|
|
reports.push(
|
|
|
|
Report::build(ReportKind::Error, token.source(), token.start())
|
2024-07-24 13:20:29 +02:00
|
|
|
.with_message("Invalid Raw Code Property")
|
|
|
|
.with_label(
|
|
|
|
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)
|
|
|
|
))
|
|
|
|
.with_color(parser.colors().warning),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
|
|
|
return reports;
|
|
|
|
}
|
2024-07-24 09:05:57 +02:00
|
|
|
PropertyMapError::NotFoundError(err) => {
|
|
|
|
reports.push(
|
|
|
|
Report::build(ReportKind::Error, token.source(), token.start())
|
2024-07-24 13:20:29 +02:00
|
|
|
.with_message("Invalid Code Property")
|
|
|
|
.with_label(
|
|
|
|
Label::new((
|
|
|
|
token.source().clone(),
|
|
|
|
token.start() + 1..token.end(),
|
|
|
|
))
|
|
|
|
.with_message(format!(
|
|
|
|
"Property `{}` is missing",
|
|
|
|
err.fg(parser.colors().info)
|
|
|
|
))
|
|
|
|
.with_color(parser.colors().warning),
|
|
|
|
)
|
|
|
|
.finish(),
|
|
|
|
);
|
|
|
|
return reports;
|
2024-07-24 09:05:57 +02:00
|
|
|
}
|
2024-07-24 13:20:29 +02:00
|
|
|
},
|
2024-07-19 11:52:12 +02:00
|
|
|
};
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
parser.push(
|
|
|
|
document,
|
|
|
|
Box::new(Raw {
|
|
|
|
location: token.clone(),
|
|
|
|
kind: raw_kind,
|
|
|
|
content: raw_content,
|
|
|
|
}),
|
|
|
|
);
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
reports
|
|
|
|
}
|
2024-07-21 15:56:56 +02:00
|
|
|
|
2024-07-30 11:01:22 +02:00
|
|
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
2024-07-23 14:04:57 +02:00
|
|
|
let mut bindings = vec![];
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
bindings.push((
|
|
|
|
"push".to_string(),
|
|
|
|
lua.create_function(|_, (kind, content): (String, String)| {
|
|
|
|
// Validate kind
|
|
|
|
let kind = match ElemKind::from_str(kind.as_str()) {
|
|
|
|
Ok(kind) => kind,
|
|
|
|
Err(e) => {
|
|
|
|
return Err(BadArgument {
|
|
|
|
to: Some("push".to_string()),
|
|
|
|
pos: 1,
|
|
|
|
name: Some("kind".to_string()),
|
|
|
|
cause: Arc::new(mlua::Error::external(format!(
|
|
|
|
"Wrong section kind specified: {e}"
|
|
|
|
))),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CTX.with_borrow(|ctx| {
|
|
|
|
ctx.as_ref().map(|ctx| {
|
|
|
|
ctx.parser.push(
|
|
|
|
ctx.document,
|
|
|
|
Box::new(Raw {
|
|
|
|
location: ctx.location.clone(),
|
|
|
|
kind,
|
|
|
|
content,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
));
|
|
|
|
|
2024-07-30 11:01:22 +02:00
|
|
|
Some(bindings)
|
2024-07-24 13:20:29 +02:00
|
|
|
}
|
2024-07-19 11:52:12 +02:00
|
|
|
}
|
2024-07-26 18:18:28 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::compiler::compiler::Target;
|
|
|
|
use crate::parser::langparser::LangParser;
|
|
|
|
use crate::parser::source::SourceFile;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn raw_tests() {
|
|
|
|
let source = Rc::new(SourceFile::with_content(
|
|
|
|
"".to_string(),
|
|
|
|
r#"
|
|
|
|
Break{?[kind=block]<RAW>?}NewParagraph
|
|
|
|
"#
|
|
|
|
.to_string(),
|
|
|
|
None,
|
|
|
|
));
|
|
|
|
let parser = LangParser::default();
|
|
|
|
let compiler = Compiler::new(Target::HTML, None);
|
|
|
|
let doc = parser.parse(source, None);
|
|
|
|
|
|
|
|
let borrow = doc.content().borrow();
|
|
|
|
let found = borrow
|
|
|
|
.iter()
|
|
|
|
.filter_map(|e| e.downcast_ref::<Raw>())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
assert_eq!(found[0].compile(&compiler, &*doc), Ok("<RAW>".to_string()));
|
|
|
|
//assert_eq!(found[1].compile(&compiler, &*doc), Ok("<RAW>".to_string()));
|
|
|
|
}
|
|
|
|
}
|