Skip to main content

matugen_parser/engine/
replace.rs

1use chumsky::span::SimpleSpan;
2use colorsys::{ColorAlpha, Hsl, Rgb};
3use indexmap::IndexMap;
4
5use crate::{
6    color::format::{format_alpha_hex, format_alpha_hex_stripped},
7    {
8        BinaryOperatorError, Error, FilterError, FilterReturnType, IfError, KeywordError,
9        LoopError, ParseErrorKind, SpannedValue, Value,
10        engine::{BinaryOperator, Expression, SpannedBinaryOperator, SpannedExpr, Template},
11    },
12};
13
14use crate::color::format::{
15    format_hex, format_hex_alpha, format_hex_alpha_stripped, format_hex_stripped, format_hsl,
16    format_hsla, format_rgb, format_rgba,
17};
18
19use super::Engine;
20
21pub const FORMATS: &[&str] = &[
22    "hex",
23    "hex_stripped",
24    "hex_alpha",
25    "hex_alpha_stripped",
26    "alpha_hex",
27    "alpha_hex_stripped",
28    "rgb",
29    "rgba",
30    "hsl",
31    "hsla",
32    "red",
33    "green",
34    "blue",
35    "alpha",
36    "hue",
37    "saturation",
38    "lightness",
39];
40
41pub fn get_str<'a>(source: &'a str, span: &SimpleSpan) -> &'a str {
42    &source[span.start..span.end]
43}
44
45pub fn get_str_vec<'a>(source: &'a str, spans: &Vec<SimpleSpan>) -> Vec<&'a str> {
46    spans
47        .iter()
48        .map(|s| get_str(source, s))
49        .collect::<Vec<&str>>()
50}
51
52// TODO: Clean both of these up
53pub fn format_color(base_color: Rgb, format: &str) -> Option<Value> {
54    let hsl_color = Hsl::from(&base_color);
55
56    match format {
57        f if FORMATS.contains(&f) => match f {
58            "hex" => Some(format_hex(&base_color).into()),
59            "hex_stripped" => Some(format_hex_stripped(&base_color).into()),
60            "hex_alpha" => Some(format_hex_alpha(&base_color).into()),
61            "hex_alpha_stripped" => Some(format_hex_alpha_stripped(&base_color).into()),
62            "alpha_hex" => Some(format_alpha_hex(&base_color).into()),
63            "alpha_hex_stripped" => Some(format_alpha_hex_stripped(&base_color).into()),
64            "rgb" => Some(format_rgb(&base_color).into()),
65            "rgba" => Some(format_rgba(&base_color).into()),
66            "hsl" => Some(format_hsl(&hsl_color).into()),
67            "hsla" => Some(format_hsla(&hsl_color).into()),
68            "red" => Some(Value::Int(base_color.red() as i64)),
69            "green" => Some(Value::Int(base_color.green() as i64)),
70            "blue" => Some(Value::Int(base_color.blue() as i64)),
71            "alpha" => Some(Value::Float(base_color.alpha())),
72            "hue" => Some(Value::Int(hsl_color.hue() as i64)),
73            "saturation" => Some(Value::Int(hsl_color.saturation() as i64)),
74            "lightness" => Some(Value::Int(hsl_color.lightness() as i64)),
75            _ => unreachable!(),
76        },
77        _ => None,
78    }
79}
80
81pub fn format_color_all(base_color: Rgb) -> IndexMap<String, Value> {
82    let hsl_color = Hsl::from(&base_color);
83
84    let mut map = IndexMap::new();
85
86    map.insert("hex".to_string(), Value::Ident(format_hex(&base_color)));
87    map.insert(
88        "hex_stripped".to_string(),
89        Value::Ident(format_hex_stripped(&base_color)),
90    );
91    map.insert(
92        "hex_alpha".to_string(),
93        Value::Ident(format_hex_alpha(&base_color)),
94    );
95    map.insert(
96        "hex_alpha_stripped".to_string(),
97        Value::Ident(format_hex_alpha_stripped(&base_color)),
98    );
99    map.insert("rgb".to_string(), Value::Ident(format_rgb(&base_color)));
100    map.insert("rgba".to_string(), Value::Ident(format_rgba(&base_color)));
101    map.insert("hsl".to_string(), Value::Ident(format_hsl(&hsl_color)));
102    map.insert("hsla".to_string(), Value::Ident(format_hsla(&hsl_color)));
103    map.insert("red".to_string(), Value::Int(base_color.red() as i64));
104    map.insert("green".to_string(), Value::Int(base_color.green() as i64));
105    map.insert("blue".to_string(), Value::Int(base_color.blue() as i64));
106    map.insert(
107        "alpha".to_string(),
108        Value::Ident(format!("{:?}", base_color.alpha() as u8)),
109    );
110    map.insert(
111        "hue".to_string(),
112        Value::Ident(format!("{:?}", &hsl_color.hue())),
113    );
114    map.insert(
115        "saturation".to_string(),
116        Value::Ident(format!("{:?}", &hsl_color.saturation())),
117    );
118    map.insert(
119        "lightness".to_string(),
120        Value::Ident(format!("{:?}", &hsl_color.lightness())),
121    );
122
123    map
124}
125
126impl Engine {
127    pub fn generate_template(&self, template: &Template, name: String) -> String {
128        self.build_string(&template.ast, &self.sources[template.source_id], &name)
129    }
130
131    fn build_string(&self, exprs: &[Box<SpannedExpr>], source: &String, name: &str) -> String {
132        let src = &mut String::from("");
133
134        for expr in exprs.iter() {
135            let _range = expr.span.into_range();
136
137            self.eval(src, expr, source, name);
138        }
139
140        src.to_string()
141    }
142
143    fn eval(&self, src: &mut String, expr: &SpannedExpr, source: &String, name: &str) {
144        match &expr.expr {
145            Expression::Keyword { keywords } => {
146                src.push_str(
147                    &self
148                        .get_value(keywords, source, true, false, name)
149                        .to_string(),
150                );
151            }
152            Expression::KeywordWithFilters { keyword, filters } => {
153                let value = self.get_value(keyword, source, false, false, name);
154                let keywords = keyword.expr.as_keywords(source);
155
156                src.push_str(
157                    &self
158                        .get_replacement_filter(
159                            value.into(),
160                            keywords.as_deref(),
161                            filters,
162                            source,
163                            keyword.span,
164                            name,
165                            true,
166                        )
167                        .to_string(),
168                );
169            }
170            Expression::Raw { value } => {
171                let str = get_str(source, value);
172                src.push_str(str);
173            }
174            Expression::ForLoop { var, iter, body } => {
175                let format_color = true;
176
177                match &iter.expr {
178                    //     Expression::KeywordWithFilters { keyword, filters } => todo!(),
179                    Expression::Range { start, end } => {
180                        for (index, i) in (*start..*end).enumerate() {
181                            self.runtime.borrow_mut().push_scope();
182
183                            let total = (*end - *start) as usize;
184
185                            self.add_loop_variables(index, total);
186
187                            self.runtime.borrow_mut().insert("i", Value::Int(i));
188
189                            src.push_str(&self.eval_loop_body(body.clone(), source, name));
190                            self.runtime.borrow_mut().pop_scope();
191                        }
192                    }
193                    Expression::Access { keywords: _ } => {
194                        let values = match iter.expr.as_keywords(source) {
195                            Some(v) => self.resolve_path(v, format_color, expr.span, name),
196                            None => unreachable!(),
197                        };
198
199                        let Ok(values) = values else {
200                            let spans = iter.expr.as_spans().unwrap();
201                            let error = Error::ResolveError {
202                                span: SimpleSpan::from(
203                                    spans.first().unwrap().start..spans.last().unwrap().end,
204                                ),
205                                name: name.to_string(),
206                            };
207                            self.errors.add(error);
208                            return;
209                        };
210
211                        match values {
212                            Value::Map(map) => {
213                                let res = self.eval_map(map, body, var, source, iter.span, name);
214                                src.push_str(&res);
215                            }
216                            Value::LazyColor { color, scheme: _ } | Value::Color(color) => {
217                                let formats = format_color_all(color);
218                                let res =
219                                    self.eval_map(formats, body, var, source, iter.span, name);
220                                src.push_str(&res);
221                            }
222                            Value::Array(arr) => {
223                                let total = arr.len();
224
225                                for (index, item) in arr.iter().enumerate() {
226                                    self.runtime.borrow_mut().push_scope();
227
228                                    if var.len() == 1 {
229                                        self.runtime
230                                            .borrow_mut()
231                                            .insert(var[0].value.to_string(), item.clone());
232                                    } else {
233                                        self.errors.add(Error::ParseError {
234                                            kind: ParseErrorKind::Loop(
235                                                LoopError::TooManyLoopVariablesArray,
236                                            ),
237                                            span: iter.span,
238                                            name: name.to_string(),
239                                        });
240                                    }
241
242                                    self.add_loop_variables(index, total);
243
244                                    src.push_str(&self.eval_loop_body(body.clone(), source, name));
245                                    self.runtime.borrow_mut().pop_scope();
246                                }
247                            }
248                            _ => {
249                                self.errors.add(Error::ParseError {
250                                    kind: ParseErrorKind::Loop(
251                                        crate::LoopError::LoopOverNonIterableValue,
252                                    ),
253                                    span: iter.span,
254                                    name: name.to_string(),
255                                });
256                            }
257                        }
258                    }
259                    _ => {}
260                }
261            }
262            Expression::Include { name: include_name } => match &include_name.value {
263                Value::Ident(s) => {
264                    let template = self.templates.get(s);
265                    match template {
266                        Some(v) => {
267                            let res = self.build_string(&v.ast, &self.sources[v.source_id], s);
268                            src.push_str(&res);
269                        }
270                        None => {
271                            let error = Error::IncludeError {
272                                span: include_name.span,
273                                name: name.to_string(),
274                            };
275                            self.errors.add(error);
276                            return;
277                        }
278                    };
279                }
280                _ => {}
281            },
282            Expression::If { .. } => {
283                let value = &self.get_value(expr, source, true, false, name);
284
285                match value {
286                    Value::Null => {}
287                    _ => src.push_str(&value.to_string()),
288                }
289            }
290            Expression::Filter { name: _, args: _ } => unreachable!(),
291            Expression::Range { start: _, end: _ } => unreachable!(),
292            Expression::LiteralValue { value: _ } => unreachable!(),
293            Expression::BinaryOp {
294                lhs: _,
295                op: _,
296                rhs: _,
297            } => unreachable!(),
298            Expression::Access { keywords: _ } => unreachable!(),
299        }
300    }
301
302    fn add_loop_variables(&self, index: usize, total: usize) {
303        let is_first = index == 0;
304        let is_last = index == total - 1;
305
306        let mut map = IndexMap::new();
307        map.insert("index".to_string(), Value::Int(index as i64));
308        map.insert("first".to_string(), Value::Bool(is_first));
309        map.insert("last".to_string(), Value::Bool(is_last));
310
311        self.runtime.borrow_mut().insert("loop", Value::Map(map));
312    }
313
314    fn eval_map(
315        &self,
316        map: IndexMap<String, Value>,
317        body: &Vec<Box<SpannedExpr>>,
318        var: &Vec<SpannedValue>,
319        source: &String,
320        span: SimpleSpan,
321        name: &str,
322    ) -> String {
323        let mut output = String::from("");
324        let total = map.len();
325
326        for (index, (key, value)) in map.iter().enumerate() {
327            self.runtime.borrow_mut().push_scope();
328
329            if var.len() == 1 {
330                self.runtime
331                    .borrow_mut()
332                    .insert(var[0].value.to_string(), Value::Ident(key.clone()));
333            } else if var.len() == 2 {
334                self.runtime
335                    .borrow_mut()
336                    .insert(var[0].value.to_string(), Value::Ident(key.clone()));
337                self.runtime
338                    .borrow_mut()
339                    .insert(var[1].value.to_string(), value.clone());
340            } else {
341                self.errors.add(Error::ParseError {
342                    kind: ParseErrorKind::Loop(LoopError::TooManyLoopVariables),
343                    span: span,
344                    name: name.to_string(),
345                });
346            }
347
348            self.add_loop_variables(index, total);
349
350            output.push_str(&self.eval_loop_body(body.clone(), source, name));
351
352            self.runtime.borrow_mut().pop_scope();
353        }
354        output
355    }
356
357    fn eval_loop_body(&self, exprs: Vec<Box<SpannedExpr>>, source: &String, name: &str) -> String {
358        let mut output = String::from("");
359
360        for expr in exprs.into_iter() {
361            let _range = expr.span.into_range();
362            self.eval(&mut output, &expr, source, name);
363        }
364
365        output
366    }
367
368    fn get_replacement(
369        &self,
370        keywords: &[&str],
371        span: SimpleSpan,
372        format_value: bool,
373        get_color_value: bool,
374        name: &str,
375    ) -> Value {
376        match self.resolve_path(keywords.iter().copied(), format_value, span, name) {
377            Ok(v) => {
378                if get_color_value {
379                    match v {
380                        Value::Color(c) => format_color(c, self.get_format(keywords)).unwrap(),
381                        Value::HslColor(c) => {
382                            format_color(c.into(), self.get_format(keywords)).unwrap()
383                        }
384                        Value::LazyColor { color: c, .. } => {
385                            format_color(c, self.get_format(keywords)).unwrap()
386                        }
387                        _ => v,
388                    }
389                } else {
390                    v
391                }
392            }
393            Err(e) => {
394                self.errors.add(e);
395
396                if keywords[0] == "colors" {
397                    Value::Color(Rgb::from_hex_str("#ffffff").unwrap())
398                } else {
399                    Value::Ident(String::from(""))
400                }
401            }
402        }
403    }
404
405    fn replace_binary_op(
406        &self,
407        lhs: &Box<SpannedExpr>,
408        op: SpannedBinaryOperator,
409        rhs: &Box<SpannedExpr>,
410        source: &String,
411        span: SimpleSpan,
412        name: &str,
413    ) -> Value {
414        let left = self.get_value(lhs, source, false, true, name);
415        let right = self.get_value(rhs, source, false, true, name);
416
417        let left_val = left.get_float();
418        let right_val = right.get_float();
419
420        match (left_val, right_val) {
421            (Some(l), Some(r)) => self.apply_binary_op(l, r, op.op),
422            (l, r) => {
423                if l.is_none() | r.is_none() {
424                    self.errors.add(Error::ParseError {
425                        kind: ParseErrorKind::BinOp(
426                            BinaryOperatorError::InvalidBinaryOperatorType {
427                                lhs: left.to_string(),
428                                op: op.op.to_string(),
429                                rhs: right.to_string(),
430                            },
431                        ),
432                        span,
433                        name: name.to_string(),
434                    });
435                }
436
437                Value::Int(0)
438            }
439        }
440    }
441
442    fn normalize_number(&self, v: f64) -> Value {
443        if v.fract() == 0.0 {
444            Value::Int(v as i64)
445        } else {
446            Value::Float(v)
447        }
448    }
449
450    fn apply_binary_op(&self, left: f64, right: f64, op: BinaryOperator) -> Value {
451        let v = match op {
452            BinaryOperator::Add => left + right,
453            BinaryOperator::Sub => left - right,
454            BinaryOperator::Mul => left * right,
455            BinaryOperator::Div => left / right,
456        };
457
458        self.normalize_number(v)
459    }
460
461    fn get_value(
462        &self,
463        expr: &SpannedExpr,
464        source: &String,
465        format_value: bool,
466        get_color_value: bool,
467        name: &str,
468    ) -> Value {
469        match &expr.expr {
470            Expression::Keyword { keywords } => {
471                self.get_value(&keywords, source, format_value, get_color_value, name)
472            }
473            Expression::KeywordWithFilters { keyword, filters } => {
474                let value = self.get_value(&keyword, source, false, get_color_value, name);
475                let keywords = keyword.expr.as_keywords(source);
476                Value::from(self.get_replacement_filter(
477                    value.into(),
478                    keywords.as_deref(),
479                    filters,
480                    source,
481                    keyword.span,
482                    name,
483                    format_value,
484                ))
485            }
486            Expression::LiteralValue { value } => value.value.clone(),
487            Expression::Access { keywords } => self.get_replacement(
488                &get_str_vec(source, keywords),
489                expr.span,
490                format_value,
491                get_color_value,
492                name,
493            ),
494            Expression::BinaryOp { lhs, op, rhs } => {
495                self.replace_binary_op(lhs, *op, rhs, source, expr.span, name)
496            }
497            Expression::Raw { value } => Value::Ident(get_str(source, value).to_string()),
498            Expression::If {
499                condition,
500                then_branch,
501                else_branch,
502                negated,
503            } => {
504                let bool = match self.get_value(condition, source, false, get_color_value, name) {
505                    Value::Bool(b) => {
506                        if *negated {
507                            !b
508                        } else {
509                            b
510                        }
511                    }
512                    _ => {
513                        self.errors.add(Error::ParseError {
514                            kind: ParseErrorKind::If(IfError::InvalidIfCondition),
515                            span: expr.span,
516                            name: name.to_string(),
517                        });
518                        true
519                    }
520                };
521                let mut values = vec![];
522
523                if bool {
524                    if format_value {
525                        let str = self.build_string(&then_branch, source, name);
526                        return Value::Ident(str);
527                    } else {
528                        for expr in then_branch {
529                            values.push(self.get_value(
530                                expr,
531                                source,
532                                format_value,
533                                get_color_value,
534                                name,
535                            ))
536                        }
537                    }
538
539                    return Value::Array(values);
540                } else {
541                    if let Some(exprs) = else_branch {
542                        if format_value {
543                            let str = self.build_string(&exprs, source, name);
544                            return Value::Ident(str);
545                        } else {
546                            for expr in exprs {
547                                values.push(self.get_value(
548                                    expr,
549                                    source,
550                                    format_value,
551                                    get_color_value,
552                                    name,
553                                ))
554                            }
555                        }
556                        return Value::Array(values);
557                    } else {
558                        return Value::Null;
559                    };
560                }
561            }
562            _ => {
563                dbg!(&expr);
564                panic!("");
565            }
566        }
567    }
568
569    fn get_replacement_filter(
570        &self,
571        mut current_value: FilterReturnType,
572        keywords: Option<&[&str]>,
573        filters: &[SpannedExpr],
574        source: &String,
575        span: SimpleSpan,
576        name: &str,
577        format_value: bool,
578    ) -> FilterReturnType {
579        let is_color = match &current_value {
580            FilterReturnType::Rgb(_) => true,
581            FilterReturnType::Hsl(_) => true,
582            FilterReturnType::String(_) => false,
583            FilterReturnType::Bool(_) => false,
584        };
585
586        let (format, is_format_empty) = match keywords {
587            Some(v) => (self.get_format(v), false),
588            None => ("hex", true),
589        };
590
591        for filter in filters {
592            if let Expression::Filter {
593                name: filter_name,
594                args,
595            } = &filter.expr
596            {
597                let mut args_resolved = vec![];
598                for arg in args {
599                    match &arg.expr {
600                        Expression::Keyword { keywords } => args_resolved.push(SpannedValue {
601                            value: self.get_value(keywords, source, false, false, name),
602                            span: arg.span,
603                        }),
604                        Expression::KeywordWithFilters { keyword, filters } => {
605                            let value = self.get_value(&keyword, source, false, false, name);
606                            let keywords = keyword.expr.as_keywords(source);
607                            args_resolved.push(SpannedValue {
608                                value: self
609                                    .get_replacement_filter(
610                                        value.into(),
611                                        keywords.as_deref(),
612                                        filters,
613                                        source,
614                                        span,
615                                        name,
616                                        false,
617                                    )
618                                    .into(),
619                                span: arg.span,
620                            });
621                        }
622                        Expression::LiteralValue { value } => args_resolved.push(value.clone()),
623                        Expression::BinaryOp { lhs, op, rhs } => {
624                            args_resolved.push(SpannedValue {
625                                value: self
626                                    .replace_binary_op(lhs, *op, rhs, source, arg.span, name),
627                                span: arg.span,
628                            });
629                        }
630                        Expression::If { .. } => {
631                            let val = self.get_value(arg, source, false, false, name);
632                            match val {
633                                Value::Array(array) => {
634                                    for value in array {
635                                        args_resolved.push(SpannedValue {
636                                            value,
637                                            span: arg.span,
638                                        })
639                                    }
640                                }
641                                v => {
642                                    args_resolved.push(SpannedValue {
643                                        value: v,
644                                        span: arg.span,
645                                    });
646                                }
647                            }
648                        }
649                        _ => {
650                            panic!("Unsupported filter arg")
651                        }
652                    }
653                }
654
655                let filter_name = get_str(source, filter_name);
656
657                let (is_alpha_filter_invalid, format_replacement): (bool, Option<&'static str>) =
658                    match format {
659                        "hex" => (
660                            true,
661                            Some("hex_alpha, hex_alpha_stripped, alpha_hex or alpha_hex_stripped"),
662                        ),
663                        "rgb" => (true, Some("rgba")),
664                        "hsl" => (true, Some("hsla")),
665                        _ => (false, None),
666                    };
667
668                if filter_name == "set_alpha" && is_alpha_filter_invalid && !is_format_empty {
669                    let error = Error::ParseError {
670                        kind: ParseErrorKind::Filter(FilterError::SetAlphaOnNonAlphaFormat {
671                            replacement: format_replacement.unwrap(),
672                        }),
673                        span: filter.span,
674                        name: name.to_string(),
675                    };
676                    self.errors.add(error);
677                }
678
679                current_value = match self.apply_filter(
680                    filter_name,
681                    &args_resolved,
682                    keywords.unwrap_or(&vec![]),
683                    current_value,
684                    filter.span,
685                    name,
686                ) {
687                    Ok(val) => val,
688                    Err(e) => {
689                        let error = Error::ParseError {
690                            kind: ParseErrorKind::Filter(e),
691                            span: filter.span,
692                            name: name.to_string(),
693                        };
694                        self.errors.add(error);
695
696                        match &is_color {
697                            false => FilterReturnType::from(String::from("")),
698                            true => FilterReturnType::from(Rgb::from_hex_str("#ffffff").unwrap()),
699                        }
700                    }
701                };
702            }
703        }
704
705        if !format_value {
706            return current_value;
707        }
708
709        match current_value {
710            FilterReturnType::String(_) => current_value,
711            FilterReturnType::Rgb(argb) => match format_color(argb, format) {
712                Some(v) => FilterReturnType::String(v.to_string()),
713                None => {
714                    let error = Error::ParseError {
715                        kind: ParseErrorKind::Keyword(KeywordError::InvalidFormat {
716                            formats: FORMATS,
717                        }),
718                        span,
719                        name: name.to_string(),
720                    };
721                    self.errors.add(error);
722                    FilterReturnType::String(String::from(""))
723                }
724            },
725            FilterReturnType::Hsl(hsl) => match format_color(hsl.into(), format) {
726                Some(v) => FilterReturnType::String(v.to_string()),
727                None => {
728                    let error = Error::ParseError {
729                        kind: ParseErrorKind::Keyword(KeywordError::InvalidFormat {
730                            formats: FORMATS,
731                        }),
732                        span,
733                        name: name.to_string(),
734                    };
735                    self.errors.add(error);
736                    FilterReturnType::String(String::from(""))
737                }
738            },
739            FilterReturnType::Bool(_) => current_value,
740        }
741    }
742
743    fn apply_filter(
744        &self,
745        filtername: &str,
746        args: &[SpannedValue],
747        keywords: &[&str],
748        input: FilterReturnType,
749        span: SimpleSpan,
750        name: &str,
751    ) -> Result<FilterReturnType, FilterError> {
752        match self.filters.get(filtername) {
753            Some(f) => f(keywords, args, input, self),
754            None => {
755                let error = Error::ParseError {
756                    kind: ParseErrorKind::Filter(FilterError::FilterNotFound {
757                        filter: filtername.to_owned(),
758                    }),
759                    span,
760                    name: name.to_string(),
761                };
762                self.errors.add(error);
763                Ok(FilterReturnType::from(
764                    // This is a color so that when you chain filters that aren't found, it wont return FilterError::ColorFilterOnString
765                    Rgb::from_hex_str("#ffffff").unwrap(),
766                ))
767            }
768        }
769    }
770}