1use super::Expression;
2use serde::Deserialize;
3
4#[doc(inline)]
6pub use hcl_primitives::expr::{BinaryOperator, UnaryOperator};
7
8#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
10pub enum Operation {
11 Unary(UnaryOp),
13 Binary(BinaryOp),
15}
16
17impl From<UnaryOp> for Operation {
18 fn from(op: UnaryOp) -> Self {
19 Operation::Unary(op)
20 }
21}
22
23impl From<BinaryOp> for Operation {
24 fn from(op: BinaryOp) -> Self {
25 Operation::Binary(op)
26 }
27}
28
29#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
31pub struct UnaryOp {
32 pub operator: UnaryOperator,
34 pub expr: Expression,
36}
37
38impl UnaryOp {
39 pub fn new<T>(operator: UnaryOperator, expr: T) -> UnaryOp
41 where
42 T: Into<Expression>,
43 {
44 UnaryOp {
45 operator,
46 expr: expr.into(),
47 }
48 }
49}
50
51#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
53pub struct BinaryOp {
54 pub lhs_expr: Expression,
56 pub operator: BinaryOperator,
58 pub rhs_expr: Expression,
60}
61
62impl BinaryOp {
63 pub fn new<L, R>(lhs_expr: L, operator: BinaryOperator, rhs_expr: R) -> BinaryOp
65 where
66 L: Into<Expression>,
67 R: Into<Expression>,
68 {
69 BinaryOp {
70 lhs_expr: lhs_expr.into(),
71 operator,
72 rhs_expr: rhs_expr.into(),
73 }
74 }
75
76 pub(crate) fn normalize(self) -> BinaryOp {
80 use Operand::{BinOp, Expr};
81
82 enum Operand {
86 BinOp(BinaryOp),
87 Expr(Expression),
88 }
89
90 impl From<Expression> for Operand {
91 fn from(expr: Expression) -> Self {
92 match expr {
93 Expression::Operation(operation) => match *operation {
94 Operation::Binary(binary) => Operand::BinOp(binary),
95 unary => Operand::Expr(Expression::from(unary)),
96 },
97 expr => Operand::Expr(expr),
98 }
99 }
100 }
101
102 let lhs = Operand::from(self.lhs_expr);
103 let operator = self.operator;
104 let rhs = Operand::from(self.rhs_expr);
105
106 match (lhs, rhs) {
107 (BinOp(lhs), BinOp(rhs)) => normalize_both(lhs.normalize(), operator, rhs.normalize()),
108 (BinOp(lhs), Expr(rhs)) => normalize_lhs(lhs.normalize(), operator, rhs),
109 (Expr(lhs), BinOp(rhs)) => normalize_rhs(lhs, operator, rhs.normalize()),
110 (Expr(lhs), Expr(rhs)) => BinaryOp::new(lhs, operator, rhs),
111 }
112 }
113}
114
115fn normalize_both(lhs: BinaryOp, operator: BinaryOperator, rhs: BinaryOp) -> BinaryOp {
116 if lhs.operator.precedence() < operator.precedence() {
117 BinaryOp::new(
121 lhs.lhs_expr,
122 lhs.operator,
123 Operation::Binary(normalize_rhs(lhs.rhs_expr, operator, rhs)),
124 )
125 } else if rhs.operator.precedence() < operator.precedence() {
126 BinaryOp::new(
130 Operation::Binary(normalize_lhs(lhs, operator, rhs.lhs_expr)),
131 rhs.operator,
132 rhs.rhs_expr,
133 )
134 } else {
135 BinaryOp::new(Operation::Binary(lhs), operator, Operation::Binary(rhs))
137 }
138}
139
140fn normalize_lhs(lhs: BinaryOp, operator: BinaryOperator, rhs_expr: Expression) -> BinaryOp {
141 if lhs.operator.precedence() < operator.precedence() {
142 BinaryOp::new(
146 lhs.lhs_expr,
147 lhs.operator,
148 Operation::Binary(BinaryOp::new(lhs.rhs_expr, operator, rhs_expr)),
149 )
150 } else {
151 BinaryOp::new(Operation::Binary(lhs), operator, rhs_expr)
153 }
154}
155
156fn normalize_rhs(lhs_expr: Expression, operator: BinaryOperator, rhs: BinaryOp) -> BinaryOp {
157 if rhs.operator.precedence() < operator.precedence() {
158 BinaryOp::new(
162 Operation::Binary(BinaryOp::new(lhs_expr, operator, rhs.lhs_expr)),
163 rhs.operator,
164 rhs.rhs_expr,
165 )
166 } else {
167 BinaryOp::new(lhs_expr, operator, Operation::Binary(rhs))
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use pretty_assertions::assert_eq;
176
177 macro_rules! binop {
178 ($l:expr, $op:expr, $r:expr $(,)?) => {
179 BinaryOp::new($l, $op, $r)
180 };
181 }
182
183 macro_rules! assert_normalizes_to {
184 ($op:expr, $expected:expr $(,)?) => {
185 assert_eq!($op.normalize(), $expected);
186 };
187 }
188
189 #[test]
190 fn normalize_binary_op() {
191 use BinaryOperator::{Div, Mod, Mul, Plus};
192
193 assert_normalizes_to!(
194 binop!(binop!(1, Plus, 2), Div, binop!(3, Mul, 4)),
195 binop!(1, Plus, binop!(2, Div, binop!(3, Mul, 4))),
196 );
197
198 assert_normalizes_to!(
199 binop!(binop!(1, Div, 2), Mul, binop!(3, Plus, binop!(4, Mod, 5))),
200 binop!(binop!(binop!(1, Div, 2), Mul, 3), Plus, binop!(4, Mod, 5)),
201 );
202
203 assert_normalizes_to!(
204 binop!(binop!(binop!(1, Plus, 2), Mul, 3), Div, 4),
205 binop!(1, Plus, binop!(binop!(2, Mul, 3), Div, 4)),
206 );
207
208 assert_normalizes_to!(
209 binop!(1, Div, binop!(binop!(2, Plus, 3), Mul, 4)),
210 binop!(binop!(1, Div, 2), Plus, binop!(3, Mul, 4)),
211 );
212 }
213
214 #[test]
215 fn normalize_parenthesized() {
216 use BinaryOperator::{Div, Mod, Mul, Plus};
217
218 fn parens(op: BinaryOp) -> Expression {
219 Expression::Parenthesis(Box::new(op.into()))
220 }
221
222 let op = binop!(
223 parens(binop!(1, Div, 2)),
224 Mul,
225 parens(binop!(3, Mod, parens(binop!(4, Plus, 5)))),
226 );
227
228 assert_normalizes_to!(op.clone(), op);
229 }
230
231 #[test]
232 fn already_normalized() {
233 use BinaryOperator::Plus;
234
235 let op = binop!(binop!(1, Plus, 2), Plus, binop!(3, Plus, 4));
236
237 assert_normalizes_to!(op.clone(), op);
238 }
239}