1use crate::analysis::expr_ir::ExprIr;
2use squawk_syntax::ast::{AstNode, Expr};
3
4pub struct ExprVisitor;
5
6impl ExprVisitor {
7 pub fn convert(expr: Expr) -> ExprIr {
8 match expr {
9 Expr::Literal(lit) => Self::convert_literal(lit),
10 Expr::NameRef(nr) => Self::convert_name_ref(nr),
11 Expr::CallExpr(ce) => Self::convert_call_expr(ce),
12 Expr::BinExpr(be) => Self::convert_bin_expr(be),
13 Expr::CastExpr(ce) => Self::convert_cast_expr(ce),
14 Expr::PrefixExpr(pe) => pe
15 .expr()
16 .map(Self::convert)
17 .unwrap_or(ExprIr::Literal("<prefix>".into())),
18 Expr::ParenExpr(pe) => pe
19 .expr()
20 .map(Self::convert)
21 .unwrap_or(ExprIr::Literal("<paren>".into())),
22 Expr::CaseExpr(ce) => Self::convert_case_expr(ce),
23 Expr::ArrayExpr(ae) => Self::convert_array_expr(ae),
24 Expr::BetweenExpr(be) => Self::convert_between_expr(be),
25 Expr::IndexExpr(ie) => Self::convert_index_expr(ie),
26 Expr::SliceExpr(se) => Self::convert_slice_expr(se),
27 Expr::FieldExpr(fe) => Self::convert_field_expr(fe),
28 Expr::PostfixExpr(pe) => Self::convert_postfix_expr(pe),
29 Expr::Collate(ce) => {
30 let left = ce
31 .expr()
32 .map(Self::convert)
33 .unwrap_or(ExprIr::Literal("<lhs>".into()));
34 let right = ce
35 .collation_ref()
36 .map(|c| c.syntax().text().to_string())
37 .unwrap_or_else(|| "<collation>".into());
38 ExprIr::BinaryOp {
39 left: Box::new(left),
40 op: "COLLATE".to_string(),
41 right: Box::new(ExprIr::Literal(right)),
42 }
43 }
44 _ => ExprIr::Literal("<complex>".into()),
45 }
46 }
47
48 fn convert_literal(lit: squawk_syntax::ast::Literal) -> ExprIr {
49 ExprIr::Literal(lit.syntax().text().to_string())
50 }
51
52 fn convert_name_ref(nr: squawk_syntax::ast::NameRef) -> ExprIr {
53 let name = nr.text().to_string();
54 ExprIr::ColumnRef(name)
55 }
56
57 fn convert_call_expr(ce: squawk_syntax::ast::CallExpr) -> ExprIr {
58 let name = ce
59 .expr()
60 .map(|e| match e {
61 Expr::NameRef(nr) => nr.text().to_string(),
62 other => other.syntax().text().to_string(),
63 })
64 .unwrap_or_else(|| "<fn>".into());
65
66 let args = ce
67 .arg_list()
68 .map(|al| {
69 al.args()
70 .filter_map(|arg| arg.expr())
71 .map(Self::convert)
72 .collect()
73 })
74 .unwrap_or_default();
75
76 ExprIr::FunctionCall { name, args }
77 }
78
79 fn convert_bin_expr(be: squawk_syntax::ast::BinExpr) -> ExprIr {
80 let left = be
81 .lhs()
82 .map(Self::convert)
83 .unwrap_or(ExprIr::Literal("<lhs>".into()));
84 let right = be
85 .rhs()
86 .map(Self::convert)
87 .unwrap_or(ExprIr::Literal("<rhs>".into()));
88
89 use squawk_syntax::ast::BinOp;
90 let op = be
91 .op()
92 .map(|o| match o {
93 BinOp::And(t) => t.text().to_string(),
94 BinOp::Caret(t) => t.text().to_string(),
95 BinOp::ColonEq(t) => t.text().to_string(),
96 BinOp::Eq(t) => t.text().to_string(),
97 BinOp::FatArrow(t) => t.text().to_string(),
98 BinOp::Gteq(t) => t.text().to_string(),
99 BinOp::Ilike(t) => t.text().to_string(),
100 BinOp::In(t) => t.text().to_string(),
101 BinOp::Is(t) => t.text().to_string(),
102 BinOp::LAngle(t) => t.text().to_string(),
103 BinOp::Like(t) => t.text().to_string(),
104 BinOp::Lteq(t) => t.text().to_string(),
105 BinOp::Minus(t) => t.text().to_string(),
106 BinOp::Neq(t) => t.text().to_string(),
107 BinOp::Neqb(t) => t.text().to_string(),
108 BinOp::Or(t) => t.text().to_string(),
109 BinOp::Overlaps(t) => t.text().to_string(),
110 BinOp::Percent(t) => t.text().to_string(),
111 BinOp::Plus(t) => t.text().to_string(),
112 BinOp::RAngle(t) => t.text().to_string(),
113 BinOp::Slash(t) => t.text().to_string(),
114 BinOp::Star(t) => t.text().to_string(),
115 BinOp::AtTimeZone(n) => n.syntax().text().to_string(),
116 BinOp::ColonColon(n) => n.syntax().text().to_string(),
117 BinOp::CustomOp(n) => n.syntax().text().to_string(),
118 BinOp::IsDistinctFrom(n) => n.syntax().text().to_string(),
119 BinOp::IsNot(n) => n.syntax().text().to_string(),
120 BinOp::IsNotDistinctFrom(n) => n.syntax().text().to_string(),
121 BinOp::NotIlike(n) => n.syntax().text().to_string(),
122 BinOp::NotIn(n) => n.syntax().text().to_string(),
123 BinOp::NotLike(n) => n.syntax().text().to_string(),
124 BinOp::NotSimilarTo(n) => n.syntax().text().to_string(),
125 BinOp::OperatorCall(n) => n.syntax().text().to_string(),
126 BinOp::SimilarTo(n) => n.syntax().text().to_string(),
127 BinOp::Escape(t) => t.text().to_string(),
128 })
129 .unwrap_or_else(|| "<op>".into());
130
131 ExprIr::BinaryOp {
132 left: Box::new(left),
133 op,
134 right: Box::new(right),
135 }
136 }
137
138 fn convert_cast_expr(ce: squawk_syntax::ast::CastExpr) -> ExprIr {
139 let inner = ce
140 .expr()
141 .map(Self::convert)
142 .unwrap_or(ExprIr::Literal("<cast_inner>".into()));
143 let target_type = ce
144 .ty()
145 .map(|t| t.syntax().text().to_string())
146 .unwrap_or_else(|| "<type>".into());
147 ExprIr::Cast {
148 expr: Box::new(inner),
149 target_type,
150 }
151 }
152
153 fn convert_case_expr(ce: squawk_syntax::ast::CaseExpr) -> ExprIr {
154 let mut branches: Vec<ExprIr> = Vec::new();
155
156 branches.push(ce.expr().map(Self::convert).unwrap_or(ExprIr::Omitted));
157
158 if let Some(wcl) = ce.when_clause_list() {
159 for when in wcl.when_clauses() {
160 branches.push(
161 when.condition()
162 .map(Self::convert)
163 .unwrap_or(ExprIr::Omitted),
164 );
165 branches.push(when.then().map(Self::convert).unwrap_or(ExprIr::Omitted));
166 }
167 }
168
169 branches.push(
170 ce.else_clause()
171 .and_then(|ec| ec.expr())
172 .map(Self::convert)
173 .unwrap_or(ExprIr::Omitted),
174 );
175
176 ExprIr::FunctionCall {
177 name: "<case>".into(),
178 args: branches,
179 }
180 }
181
182 fn convert_array_expr(ae: squawk_syntax::ast::ArrayExpr) -> ExprIr {
183 let elements: Vec<ExprIr> = ae.exprs().map(Self::convert).collect();
184 ExprIr::FunctionCall {
185 name: "<array>".into(),
186 args: elements,
187 }
188 }
189
190 fn convert_between_expr(be: squawk_syntax::ast::BetweenExpr) -> ExprIr {
191 let args = vec![
192 be.target().map(Self::convert).unwrap_or(ExprIr::Omitted),
193 be.start().map(Self::convert).unwrap_or(ExprIr::Omitted),
194 be.end().map(Self::convert).unwrap_or(ExprIr::Omitted),
195 ];
196 ExprIr::FunctionCall {
197 name: "<between>".into(),
198 args,
199 }
200 }
201
202 fn convert_index_expr(ie: squawk_syntax::ast::IndexExpr) -> ExprIr {
203 let args = vec![
204 ie.base().map(Self::convert).unwrap_or(ExprIr::Omitted),
205 ie.index().map(Self::convert).unwrap_or(ExprIr::Omitted),
206 ];
207 ExprIr::FunctionCall {
208 name: "<index>".into(),
209 args,
210 }
211 }
212
213 fn convert_slice_expr(se: squawk_syntax::ast::SliceExpr) -> ExprIr {
214 let args = vec![
215 se.base().map(Self::convert).unwrap_or(ExprIr::Omitted),
216 se.start().map(Self::convert).unwrap_or(ExprIr::Omitted),
217 se.end().map(Self::convert).unwrap_or(ExprIr::Omitted),
218 ];
219 ExprIr::FunctionCall {
220 name: "<slice>".into(),
221 args,
222 }
223 }
224
225 fn convert_field_expr(fe: squawk_syntax::ast::FieldExpr) -> ExprIr {
226 let args = vec![
227 fe.base().map(Self::convert).unwrap_or(ExprIr::Omitted),
228 fe.field()
229 .map(|f| ExprIr::Literal(f.text().to_string()))
230 .unwrap_or(ExprIr::Omitted),
231 ];
232 ExprIr::FunctionCall {
233 name: "<field>".into(),
234 args,
235 }
236 }
237
238 fn convert_postfix_expr(pe: squawk_syntax::ast::PostfixExpr) -> ExprIr {
239 let mut args = Vec::new();
240 for child in pe.syntax().children() {
241 if let Some(expr) = Expr::cast(child) {
242 args.push(Self::convert(expr));
243 }
244 }
245 ExprIr::FunctionCall {
246 name: "<postfix>".into(),
247 args,
248 }
249 }
250}