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