nml/src/elements/raw.rs

389 lines
9.7 KiB
Rust
Raw Normal View History

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
}