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>>>; fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
/// Checks if a given style key is registered /// Checks if a given style key is registered
fn is_registered(&self, style_key: &str) -> bool { fn is_registered(&self, style_key: &str) -> bool { self.styles().contains_key(style_key) }
self.styles().contains_key(style_key)
}
/// Gets the current active style for an element /// Gets the current active style for an element
/// NOTE: Will panic if a style is not defined for a given element /// NOTE: Will panic if a style is not defined for a given element
@ -43,7 +41,7 @@ pub trait StyleHolder {
} }
/// Sets the [`style`] /// 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); 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::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use ariadne::{Fmt, Label, Report, ReportKind}; use ariadne::Fmt;
use regex::{Captures, Regex}; use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use regex::Captures;
use regex::Regex;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::{self}; use crate::document::{self};
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule; use crate::parser::rule::RegexRule;
use crate::parser::rule::Rule;
use crate::parser::source::Cursor;
use crate::parser::source::Source; use crate::parser::source::Source;
use crate::parser::source::Token; use crate::parser::source::Token;
use super::variable::VariableRule; use super::variable::VariableRule;
pub struct ElemStyleRule { pub struct ElemStyleRule {
re: [Regex; 1], start_re: Regex,
} }
impl ElemStyleRule { impl ElemStyleRule {
pub fn new() -> Self { pub fn new() -> Self {
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 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, &self,
_: usize,
parser: &dyn Parser, parser: &dyn Parser,
_document: &'a dyn Document, _document: &'a (dyn Document<'a> + 'a),
token: Token, cursor: Cursor,
matches: Captures, _match_data: Option<Box<dyn Any>>,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> { ) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>) {
let mut reports = vec![]; 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(); let trimmed = key.as_str().trim_start().trim_end();
// Check if empty // Check if empty
if trimmed.is_empty() if trimmed.is_empty() {
{
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), key.start()) Report::build(ReportKind::Error, cursor.source.clone(), key.start())
.with_message("Empty Style Key") .with_message("Empty Style Key")
.with_label( .with_label(
Label::new((token.source(), key.range())) Label::new((cursor.source.clone(), key.range()))
.with_message(format!( .with_message(format!("Expected a non-empty style key",))
"Expected a non-empty style key", .with_color(parser.colors().error),
)) )
.with_color(parser.colors().error), .finish(),
) );
.finish()); return (cursor, reports);
return reports;
} }
// Check if key exists // Check if key exists
if !parser.is_registered(trimmed) if !parser.is_registered(trimmed) {
{
reports.push( reports.push(
Report::build(ReportKind::Error, token.source(), key.start()) Report::build(ReportKind::Error, cursor.source.clone(), key.start())
.with_message("Unknown Style Key") .with_message("Unknown Style Key")
.with_label( .with_label(
Label::new((token.source(), key.range())) Label::new((cursor.source.clone(), 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()))
.with_message(format!( .with_message(format!(
"Value `{}` is not allowed: {err}", "Could not find a style with key: {}",
value.as_str().fg(parser.colors().highlight) trimmed.fg(parser.colors().info)
)) ))
.with_color(parser.colors().error), .with_color(parser.colors().error),
) )
.finish()); .finish(),
return reports; );
}
Ok(value) => value,
};
// Attempt to serialize return (cursor, reports);
match style.from_json(value_str.as_str()) }
{
Err(err) => { parser.current_style(trimmed)
reports.push( } else {
Report::build(ReportKind::Error, token.source(), value.start()) 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_message("Invalid Style Value")
.with_label( .with_label(
Label::new((token.source(), value.range())) Label::new((cursor.source.clone(), matches.get(0).unwrap().range()))
.with_message(format!( .with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}", "Unable to parse json string after style key",
value_str.fg(parser.colors().highlight), ))
style.key().fg(parser.colors().info) .with_color(parser.colors().error),
))
.with_color(parser.colors().error),
) )
.finish()); .finish(),
return reports; );
}, return (cursor, reports);
Ok(style) => style,
} }
} 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) { fn register_styles(&self, parser: &dyn Parser) {
parser.set_style(Rc::new(SectionStyle::default())); parser.set_current_style(Rc::new(SectionStyle::default()));
} }
} }