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