Better style parsing

This commit is contained in:
ef3d0c3e 2024-08-03 11:01:32 +02:00
parent 48d2064d0c
commit 8489796510
3 changed files with 197 additions and 90 deletions

View file

@ -31,9 +31,7 @@ pub trait StyleHolder {
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
/// Checks if a given style key is registered
fn is_registered(&self, style_key: &str) -> bool {
self.styles().contains_key(style_key)
}
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
@ -43,7 +41,7 @@ pub trait StyleHolder {
}
/// Sets the [`style`]
fn set_style(&self, style: Rc<dyn ElementStyle>) {
fn set_current_style(&self, style: Rc<dyn ElementStyle>) {
self.styles_mut().insert(style.key().to_string(), style);
}
}

View file

@ -1,134 +1,243 @@
use std::any::Any;
use std::ops::Range;
use std::rc::Rc;
use ariadne::{Fmt, Label, Report, ReportKind};
use regex::{Captures, Regex};
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use regex::Captures;
use regex::Regex;
use crate::document::document::Document;
use crate::document::{self};
use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule;
use crate::parser::rule::Rule;
use crate::parser::source::Cursor;
use crate::parser::source::Source;
use crate::parser::source::Token;
use super::variable::VariableRule;
pub struct ElemStyleRule {
re: [Regex; 1],
start_re: Regex,
}
impl ElemStyleRule {
pub fn new() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)@@(.*?)=((?:\\\n|.)*)").unwrap()],
start_re: Regex::new(r"(?:^|\n)@@(.*?)=\s*\{").unwrap(),
}
}
/// Finds the json substring inside aother string
pub fn json_substring(str: &str) -> Option<&str> {
let mut in_string = false;
let mut brace_depth = 0;
let mut escaped = false;
for (pos, c) in str.char_indices() {
match c {
'{' if !in_string => brace_depth += 1,
'}' if !in_string => brace_depth -= 1,
'\\' if in_string => escaped = !escaped,
'"' if !escaped => in_string = !in_string,
_ => escaped = false,
}
if brace_depth == 0 {
return Some(&str[..=pos]);
}
}
None
}
}
impl RegexRule for ElemStyleRule {
impl Rule for ElemStyleRule {
fn name(&self) -> &'static str { "Element Style" }
fn regexes(&self) -> &[regex::Regex] { &self.re }
/*
fn on_regex_match<'a>(
&self,
_: usize,
parser: &dyn Parser,
_document: &'a dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![];
fn on_regex_match<'a>(
// Get value
let new_style = if let Some(value) = matches.get(2) {
let value_str = match VariableRule::validate_value(value.as_str()) {
Err(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), value.start())
.with_message("Invalid Style Value")
.with_label(
Label::new((token.source(), value.range()))
.with_message(format!(
"Value `{}` is not allowed: {err}",
value.as_str().fg(parser.colors().highlight)
))
.with_color(parser.colors().error),
)
.finish(),
);
return reports;
}
Ok(value) => value,
};
// Attempt to serialize
match style.from_json(value_str.as_str()) {
Err(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), value.start())
.with_message("Invalid Style Value")
.with_label(
Label::new((token.source(), value.range()))
.with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}",
value_str.fg(parser.colors().highlight),
style.key().fg(parser.colors().info)
))
.with_color(parser.colors().error),
)
.finish(),
);
return reports;
}
Ok(style) => style,
}
} else {
panic!("Unknown error")
};
parser.set_style(new_style);
reports
}
*/
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re
.find_at(cursor.source.content(), cursor.pos)
.map_or(None, |m| {
Some((m.start(), Box::new([false; 0]) as Box<dyn Any>))
})
}
fn on_match<'a>(
&self,
_: usize,
parser: &dyn Parser,
_document: &'a dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
_document: &'a (dyn Document<'a> + 'a),
cursor: Cursor,
_match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let mut reports = vec![];
let matches = self
.start_re
.captures_at(cursor.source.content(), cursor.pos)
.unwrap();
let mut cursor = cursor.at(matches.get(0).unwrap().end() - 1);
let style = if let Some(key) = matches.get(1)
{
let style = if let Some(key) = matches.get(1) {
let trimmed = key.as_str().trim_start().trim_end();
// Check if empty
if trimmed.is_empty()
{
if trimmed.is_empty() {
reports.push(
Report::build(ReportKind::Error, token.source(), key.start())
.with_message("Empty Style Key")
.with_label(
Label::new((token.source(), key.range()))
.with_message(format!(
"Expected a non-empty style key",
))
.with_color(parser.colors().error),
)
.finish());
return reports;
Report::build(ReportKind::Error, cursor.source.clone(), key.start())
.with_message("Empty Style Key")
.with_label(
Label::new((cursor.source.clone(), key.range()))
.with_message(format!("Expected a non-empty style key",))
.with_color(parser.colors().error),
)
.finish(),
);
return (cursor, reports);
}
// Check if key exists
if !parser.is_registered(trimmed)
{
if !parser.is_registered(trimmed) {
reports.push(
Report::build(ReportKind::Error, token.source(), key.start())
.with_message("Unknown Style Key")
.with_label(
Label::new((token.source(), key.range()))
.with_message(format!(
"Could not find a style with key: {}",
trimmed.fg(parser.colors().info)
))
.with_color(parser.colors().error),
)
.finish());
return reports;
}
parser.current_style(trimmed)
} else { panic!("Unknown error") };
// Get value
let new_style = if let Some(value) = matches.get(2) {
let value_str = match VariableRule::validate_value(value.as_str()) {
Err(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), value.start())
.with_message("Invalid Style Value")
.with_label(
Label::new((token.source(), value.range()))
Report::build(ReportKind::Error, cursor.source.clone(), key.start())
.with_message("Unknown Style Key")
.with_label(
Label::new((cursor.source.clone(), key.range()))
.with_message(format!(
"Value `{}` is not allowed: {err}",
value.as_str().fg(parser.colors().highlight)
"Could not find a style with key: {}",
trimmed.fg(parser.colors().info)
))
.with_color(parser.colors().error),
)
.finish());
return reports;
}
Ok(value) => value,
};
)
.finish(),
);
// Attempt to serialize
match style.from_json(value_str.as_str())
{
Err(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), value.start())
return (cursor, reports);
}
parser.current_style(trimmed)
} else {
panic!("Unknown error")
};
// Get value
let new_style = match ElemStyleRule::json_substring(
&cursor.source.clone().content().as_str()[cursor.pos..],
) {
None => {
reports.push(
Report::build(ReportKind::Error, cursor.source.clone(), cursor.pos)
.with_message("Invalid Style Value")
.with_label(
Label::new((token.source(), value.range()))
.with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}",
value_str.fg(parser.colors().highlight),
style.key().fg(parser.colors().info)
))
.with_color(parser.colors().error),
Label::new((cursor.source.clone(), matches.get(0).unwrap().range()))
.with_message(format!(
"Unable to parse json string after style key",
))
.with_color(parser.colors().error),
)
.finish());
return reports;
},
Ok(style) => style,
.finish(),
);
return (cursor, reports);
}
} else { panic!("Unknown error") };
Some(json) => {
cursor = cursor.at(cursor.pos + json.len());
parser.set_style(new_style);
// Attempt to deserialize
match style.from_json(json) {
Err(err) => {
reports.push(
Report::build(ReportKind::Error, cursor.source.clone(), cursor.pos)
.with_message("Invalid Style Value")
.with_label(
Label::new((
cursor.source.clone(),
cursor.pos..cursor.pos + json.len(),
))
.with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}",
json.fg(parser.colors().highlight),
style.key().fg(parser.colors().info)
))
.with_color(parser.colors().error),
)
.finish(),
);
return (cursor, reports);
}
Ok(style) => style,
}
}
};
reports
parser.set_current_style(new_style);
(cursor, reports)
}
}

View file

@ -367,7 +367,7 @@ impl RegexRule for SectionRule {
}
fn register_styles(&self, parser: &dyn Parser) {
parser.set_style(Rc::new(SectionStyle::default()));
parser.set_current_style(Rc::new(SectionStyle::default()));
}
}