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
52pub 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::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 ¤t_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 Rgb::from_hex_str("#ffffff").unwrap(),
766 ))
767 }
768 }
769 }
770}