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;
|
2024-10-22 21:40:00 +02:00
|
|
|
use crate::lsp::semantic::Semantics;
|
2024-07-24 13:20:29 +02:00
|
|
|
use crate::lua::kernel::CTX;
|
2024-10-20 19:38:15 +02:00
|
|
|
use crate::parser::parser::ParseMode;
|
2024-08-05 18:40:17 +02:00
|
|
|
use crate::parser::parser::ParserState;
|
2024-07-24 13:20:29 +02:00
|
|
|
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)]
|
2024-08-04 19:08:49 +02:00
|
|
|
pub struct Raw {
|
|
|
|
pub location: Token,
|
|
|
|
pub kind: ElemKind,
|
|
|
|
pub content: String,
|
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-10-20 19:38:15 +02:00
|
|
|
fn compile(
|
|
|
|
&self,
|
|
|
|
_compiler: &Compiler,
|
|
|
|
_document: &dyn Document,
|
|
|
|
_cursor: usize,
|
|
|
|
) -> 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
|
|
|
}
|
|
|
|
|
2024-08-08 17:11:32 +02:00
|
|
|
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::raw")]
|
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-08-02 10:32:00 +02:00
|
|
|
properties: PropertyParser { properties: props },
|
2024-07-24 13:20:29 +02:00
|
|
|
}
|
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-10-20 19:38:15 +02:00
|
|
|
|
2024-08-08 14:12:16 +02:00
|
|
|
fn previous(&self) -> Option<&'static str> { Some("Variable Substitution") }
|
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-10-20 19:38:15 +02:00
|
|
|
fn enabled(&self, _mode: &ParseMode, _id: usize) -> bool { true }
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
fn on_regex_match(
|
|
|
|
&self,
|
|
|
|
_index: usize,
|
2024-08-06 18:58:41 +02:00
|
|
|
state: &ParserState,
|
2024-07-24 13:20:29 +02:00
|
|
|
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 `{}`",
|
2024-08-05 18:40:17 +02:00
|
|
|
"?}".fg(state.parser.colors().info),
|
|
|
|
"{?".fg(state.parser.colors().info)
|
2024-07-24 13:20:29 +02:00
|
|
|
))
|
2024-08-05 18:40:17 +02:00
|
|
|
.with_color(state.parser.colors().error),
|
2024-07-24 13:20:29 +02:00
|
|
|
)
|
|
|
|
.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")
|
2024-08-05 18:40:17 +02:00
|
|
|
.with_color(state.parser.colors().warning),
|
2024-07-24 13:20:29 +02:00
|
|
|
)
|
|
|
|
.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}"))
|
2024-08-05 18:40:17 +02:00
|
|
|
.with_color(state.parser.colors().error),
|
2024-07-24 13:20:29 +02:00
|
|
|
)
|
|
|
|
.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)
|
2024-08-05 18:40:17 +02:00
|
|
|
.with_color(state.parser.colors().error),
|
2024-07-24 13:20:29 +02:00
|
|
|
)
|
|
|
|
.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: {}",
|
2024-08-05 18:40:17 +02:00
|
|
|
prop.fg(state.parser.colors().info),
|
|
|
|
err.fg(state.parser.colors().error)
|
2024-07-24 13:20:29 +02:00
|
|
|
))
|
2024-08-05 18:40:17 +02:00
|
|
|
.with_color(state.parser.colors().warning),
|
2024-07-24 13:20:29 +02:00
|
|
|
)
|
|
|
|
.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",
|
2024-08-05 18:40:17 +02:00
|
|
|
err.fg(state.parser.colors().info)
|
2024-07-24 13:20:29 +02:00
|
|
|
))
|
2024-08-05 18:40:17 +02:00
|
|
|
.with_color(state.parser.colors().warning),
|
2024-07-24 13:20:29 +02:00
|
|
|
)
|
|
|
|
.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-08-06 18:58:41 +02:00
|
|
|
state.push(
|
2024-07-24 13:20:29 +02:00
|
|
|
document,
|
|
|
|
Box::new(Raw {
|
|
|
|
location: token.clone(),
|
|
|
|
kind: raw_kind,
|
|
|
|
content: raw_content,
|
|
|
|
}),
|
|
|
|
);
|
2024-07-19 11:52:12 +02:00
|
|
|
|
2024-10-22 21:40:00 +02:00
|
|
|
if let Some((sems, tokens)) =
|
|
|
|
Semantics::from_source(token.source(), &state.shared.semantics)
|
|
|
|
{
|
|
|
|
let range = matches.get(0).unwrap().range();
|
|
|
|
sems.add(range.start..range.start+2, tokens.raw_sep);
|
|
|
|
if let Some(props) = matches.get(1).map(|m| m.range())
|
|
|
|
{
|
|
|
|
sems.add(props.start - 1..props.start, tokens.raw_props_sep);
|
|
|
|
sems.add(props.clone(), tokens.raw_props);
|
|
|
|
sems.add(props.end..props.end + 1, tokens.raw_props_sep);
|
|
|
|
}
|
|
|
|
sems.add(matches.get(2).unwrap().range(), tokens.raw_content);
|
|
|
|
sems.add(range.end-2..range.end, tokens.raw_sep);
|
|
|
|
}
|
|
|
|
|
2024-07-24 13:20:29 +02:00
|
|
|
reports
|
|
|
|
}
|
2024-07-21 15:56:56 +02:00
|
|
|
|
2024-08-05 18:40:17 +02:00
|
|
|
fn register_bindings<'lua>(&self, lua: &'lua Lua) -> 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| {
|
2024-08-06 18:58:41 +02:00
|
|
|
ctx.state.push(
|
2024-07-24 13:20:29 +02:00
|
|
|
ctx.document,
|
|
|
|
Box::new(Raw {
|
|
|
|
location: ctx.location.clone(),
|
|
|
|
kind,
|
|
|
|
content,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
));
|
|
|
|
|
2024-08-05 18:40:17 +02:00
|
|
|
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::*;
|
2024-08-02 10:32:00 +02:00
|
|
|
use crate::elements::paragraph::Paragraph;
|
|
|
|
use crate::elements::text::Text;
|
2024-07-26 18:18:28 +02:00
|
|
|
use crate::parser::langparser::LangParser;
|
2024-08-06 18:58:41 +02:00
|
|
|
use crate::parser::parser::Parser;
|
2024-10-20 19:38:15 +02:00
|
|
|
use crate::parser::source::SourceFile;
|
2024-10-22 21:40:00 +02:00
|
|
|
use crate::{validate_document, validate_semantics};
|
2024-07-26 18:18:28 +02:00
|
|
|
|
|
|
|
#[test]
|
2024-08-02 10:32:00 +02:00
|
|
|
fn parser() {
|
2024-07-26 18:18:28 +02:00
|
|
|
let source = Rc::new(SourceFile::with_content(
|
|
|
|
"".to_string(),
|
|
|
|
r#"
|
2024-08-02 10:32:00 +02:00
|
|
|
Break{?[kind=block] Raw?}NewParagraph{?<b>?}
|
2024-07-26 18:18:28 +02:00
|
|
|
"#
|
|
|
|
.to_string(),
|
|
|
|
None,
|
|
|
|
));
|
|
|
|
let parser = LangParser::default();
|
2024-10-20 19:38:15 +02:00
|
|
|
let (doc, _) = parser.parse(
|
|
|
|
ParserState::new(&parser, None),
|
|
|
|
source,
|
|
|
|
None,
|
|
|
|
ParseMode::default(),
|
|
|
|
);
|
2024-07-26 18:18:28 +02:00
|
|
|
|
2024-08-02 10:32:00 +02:00
|
|
|
validate_document!(doc.content().borrow(), 0,
|
|
|
|
Paragraph;
|
|
|
|
Raw { kind == ElemKind::Block, content == "Raw" };
|
|
|
|
Paragraph {
|
|
|
|
Text;
|
|
|
|
Raw { kind == ElemKind::Inline, content == "<b>" };
|
|
|
|
};
|
|
|
|
);
|
2024-07-26 18:18:28 +02:00
|
|
|
}
|
2024-08-04 10:25:51 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn lua() {
|
|
|
|
let source = Rc::new(SourceFile::with_content(
|
2024-08-05 18:40:17 +02:00
|
|
|
"".to_string(),
|
|
|
|
r#"
|
2024-08-04 10:25:51 +02:00
|
|
|
Break%<nml.raw.push("block", "Raw")>%NewParagraph%<nml.raw.push("inline", "<b>")>%
|
|
|
|
"#
|
2024-08-05 18:40:17 +02:00
|
|
|
.to_string(),
|
|
|
|
None,
|
2024-08-04 10:25:51 +02:00
|
|
|
));
|
|
|
|
let parser = LangParser::default();
|
2024-10-20 19:38:15 +02:00
|
|
|
let (doc, _) = parser.parse(
|
|
|
|
ParserState::new(&parser, None),
|
|
|
|
source,
|
|
|
|
None,
|
|
|
|
ParseMode::default(),
|
|
|
|
);
|
2024-08-04 10:25:51 +02:00
|
|
|
|
|
|
|
validate_document!(doc.content().borrow(), 0,
|
|
|
|
Paragraph;
|
|
|
|
Raw { kind == ElemKind::Block, content == "Raw" };
|
|
|
|
Paragraph {
|
|
|
|
Text;
|
|
|
|
Raw { kind == ElemKind::Inline, content == "<b>" };
|
|
|
|
};
|
|
|
|
);
|
|
|
|
}
|
2024-10-22 21:40:00 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn semantic() {
|
|
|
|
let source = Rc::new(SourceFile::with_content(
|
|
|
|
"".to_string(),
|
|
|
|
r#"
|
|
|
|
{?[kind=block] Raw?}
|
|
|
|
{?<b>?}
|
|
|
|
"#
|
|
|
|
.to_string(),
|
|
|
|
None,
|
|
|
|
));
|
|
|
|
let parser = LangParser::default();
|
|
|
|
let (_, state) = parser.parse(
|
|
|
|
ParserState::new_with_semantics(&parser, None),
|
|
|
|
source.clone(),
|
|
|
|
None,
|
|
|
|
ParseMode::default(),
|
|
|
|
);
|
|
|
|
validate_semantics!(state, source.clone(), 0,
|
|
|
|
raw_sep { delta_line == 1, delta_start == 0, length == 2 };
|
|
|
|
raw_props_sep { delta_line == 0, delta_start == 2, length == 1 };
|
|
|
|
raw_props { delta_line == 0, delta_start == 1, length == 10 };
|
|
|
|
raw_props_sep { delta_line == 0, delta_start == 10, length == 1 };
|
|
|
|
raw_content { delta_line == 0, delta_start == 1, length == 4 };
|
|
|
|
raw_sep { delta_line == 0, delta_start == 4, length == 2 };
|
|
|
|
raw_sep { delta_line == 1, delta_start == 0, length == 2 };
|
|
|
|
raw_content { delta_line == 0, delta_start == 2, length == 3 };
|
|
|
|
raw_sep { delta_line == 0, delta_start == 3, length == 2 };
|
|
|
|
);
|
|
|
|
}
|
2024-07-26 18:18:28 +02:00
|
|
|
}
|