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