1use std::{iter::Iterator, sync::Arc};
2
3use codemap::{Span, Spanned};
4
5use crate::{
6 color::Color,
7 common::{BinaryOp, Brackets, Identifier, ListSeparator, QuoteKind, UnaryOp},
8 unit::Unit,
9 value::{CalculationName, Number},
10};
11
12use super::{ArgumentInvocation, AstSupportsCondition, Interpolation, InterpolationPart};
13
14#[derive(Debug, Clone)]
16pub struct Ternary(pub ArgumentInvocation);
17
18#[derive(Debug, Clone)]
19pub struct ListExpr {
20 pub elems: Vec<Spanned<AstExpr>>,
21 pub separator: ListSeparator,
22 pub brackets: Brackets,
23}
24
25#[derive(Debug, Clone)]
26pub struct FunctionCallExpr {
27 pub namespace: Option<Spanned<Identifier>>,
28 pub name: Identifier,
29 pub arguments: Arc<ArgumentInvocation>,
30 pub span: Span,
31}
32
33#[derive(Debug, Clone)]
34pub struct InterpolatedFunction {
35 pub name: Interpolation,
36 pub arguments: ArgumentInvocation,
37 pub span: Span,
38}
39
40#[derive(Debug, Clone, Default)]
41pub struct AstSassMap(pub Vec<(Spanned<AstExpr>, AstExpr)>);
42
43#[derive(Debug, Clone)]
44pub struct BinaryOpExpr {
45 pub lhs: AstExpr,
46 pub op: BinaryOp,
47 pub rhs: AstExpr,
48 pub allows_slash: bool,
49 pub span: Span,
50}
51
52#[derive(Debug, Clone)]
53pub enum AstExpr {
54 BinaryOp(Arc<BinaryOpExpr>),
55 True,
56 False,
57 Calculation {
58 name: CalculationName,
59 args: Vec<Self>,
60 },
61 Color(Arc<Color>),
62 FunctionCall(FunctionCallExpr),
63 If(Arc<Ternary>),
64 InterpolatedFunction(Arc<InterpolatedFunction>),
65 List(ListExpr),
66 Map(AstSassMap),
67 Null,
68 Number {
69 n: Number,
70 unit: Unit,
71 },
72 Paren(Arc<Self>),
73 ParentSelector,
74 String(StringExpr, Span),
75 Supports(Arc<AstSupportsCondition>),
76 UnaryOp(UnaryOp, Arc<Self>, Span),
77 Variable {
78 name: Spanned<Identifier>,
79 namespace: Option<Spanned<Identifier>>,
80 },
81}
82
83#[derive(Debug, Clone)]
86pub struct StringExpr(pub Interpolation, pub QuoteKind);
87
88impl StringExpr {
89 fn quote_inner_text(
90 text: &str,
91 quote: char,
92 buffer: &mut Interpolation,
93 is_static: bool,
95 ) {
96 let mut chars = text.chars().peekable();
97 while let Some(char) = chars.next() {
98 if char == '\n' || char == '\r' {
99 buffer.add_char('\\');
100 buffer.add_char('a');
101 if let Some(next) = chars.peek() {
102 if next.is_ascii_whitespace() || next.is_ascii_hexdigit() {
103 buffer.add_char(' ');
104 }
105 }
106 } else {
107 if char == quote
108 || char == '\\'
109 || (is_static && char == '#' && chars.peek() == Some(&'{'))
110 {
111 buffer.add_char('\\');
112 }
113 buffer.add_char(char);
114 }
115 }
116 }
117
118 fn best_quote<'a>(strings: impl Iterator<Item = &'a str>) -> char {
119 let mut contains_double_quote = false;
120 for s in strings {
121 for c in s.chars() {
122 if c == '\'' {
123 return '"';
124 }
125 if c == '"' {
126 contains_double_quote = true;
127 }
128 }
129 }
130 if contains_double_quote {
131 '\''
132 } else {
133 '"'
134 }
135 }
136
137 pub fn as_interpolation(self, is_static: bool) -> Interpolation {
138 if self.1 == QuoteKind::None {
139 return self.0;
140 }
141
142 let quote = Self::best_quote(self.0.contents.iter().filter_map(|c| match c {
143 InterpolationPart::Expr(..) => None,
144 InterpolationPart::String(text) => Some(text.as_str()),
145 }));
146
147 let mut buffer = Interpolation::new();
148 buffer.add_char(quote);
149
150 for value in self.0.contents {
151 match value {
152 InterpolationPart::Expr(e) => buffer.add_expr(e),
153 InterpolationPart::String(text) => {
154 Self::quote_inner_text(&text, quote, &mut buffer, is_static);
155 }
156 }
157 }
158
159 buffer.add_char(quote);
160
161 buffer
162 }
163}
164
165impl AstExpr {
166 pub fn is_variable(&self) -> bool {
167 matches!(self, Self::Variable { .. })
168 }
169
170 pub fn is_slash_operand(&self) -> bool {
171 match self {
172 Self::Number { .. } | Self::Calculation { .. } => true,
173 Self::BinaryOp(binop) => binop.allows_slash,
174 _ => false,
175 }
176 }
177
178 pub fn slash(left: Self, right: Self, span: Span) -> Self {
179 Self::BinaryOp(Arc::new(BinaryOpExpr {
180 lhs: left,
181 op: BinaryOp::Div,
182 rhs: right,
183 allows_slash: true,
184 span,
185 }))
186 }
187
188 pub const fn span(self, span: Span) -> Spanned<Self> {
189 Spanned { node: self, span }
190 }
191}