1use super::types::DataType;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum OperatorKind {
42 Infix,
43 Prefix,
44 Postfix,
45}
46
47#[derive(Debug, Clone, Copy)]
49pub struct OperatorEntry {
50 pub name: &'static str,
53 pub lhs_type: DataType,
57 pub rhs_type: DataType,
59 pub return_type: DataType,
61 pub kind: OperatorKind,
63}
64
65const fn infix(name: &'static str, lhs: DataType, rhs: DataType, ret: DataType) -> OperatorEntry {
66 OperatorEntry {
67 name,
68 lhs_type: lhs,
69 rhs_type: rhs,
70 return_type: ret,
71 kind: OperatorKind::Infix,
72 }
73}
74
75const fn prefix(name: &'static str, operand: DataType, ret: DataType) -> OperatorEntry {
76 OperatorEntry {
77 name,
78 lhs_type: DataType::Nullable,
79 rhs_type: operand,
80 return_type: ret,
81 kind: OperatorKind::Prefix,
82 }
83}
84
85pub const OPERATOR_CATALOG: &[OperatorEntry] = &[
88 infix("+", DataType::Integer, DataType::Integer, DataType::Integer),
90 infix("+", DataType::Integer, DataType::Float, DataType::Float),
91 infix("+", DataType::Float, DataType::Integer, DataType::Float),
92 infix("+", DataType::Float, DataType::Float, DataType::Float),
93 infix("+", DataType::BigInt, DataType::BigInt, DataType::BigInt),
94 infix("+", DataType::Decimal, DataType::Decimal, DataType::Decimal),
95 infix("-", DataType::Integer, DataType::Integer, DataType::Integer),
97 infix("-", DataType::Float, DataType::Float, DataType::Float),
98 infix("-", DataType::BigInt, DataType::BigInt, DataType::BigInt),
99 infix("-", DataType::Decimal, DataType::Decimal, DataType::Decimal),
100 prefix("-", DataType::Integer, DataType::Integer),
102 prefix("-", DataType::Float, DataType::Float),
103 prefix("-", DataType::BigInt, DataType::BigInt),
104 prefix("-", DataType::Decimal, DataType::Decimal),
105 infix("*", DataType::Integer, DataType::Integer, DataType::Integer),
107 infix("*", DataType::Float, DataType::Float, DataType::Float),
108 infix("*", DataType::BigInt, DataType::BigInt, DataType::BigInt),
109 infix("/", DataType::Integer, DataType::Integer, DataType::Float),
111 infix("/", DataType::Float, DataType::Float, DataType::Float),
112 infix("/", DataType::BigInt, DataType::BigInt, DataType::Float),
113 infix("%", DataType::Integer, DataType::Integer, DataType::Integer),
115 infix("%", DataType::BigInt, DataType::BigInt, DataType::BigInt),
116 infix("||", DataType::Text, DataType::Text, DataType::Text),
118 infix("=", DataType::Integer, DataType::Integer, DataType::Boolean),
120 infix("=", DataType::Float, DataType::Float, DataType::Boolean),
121 infix("=", DataType::Text, DataType::Text, DataType::Boolean),
122 infix("=", DataType::Boolean, DataType::Boolean, DataType::Boolean),
123 infix("=", DataType::Uuid, DataType::Uuid, DataType::Boolean),
124 infix(
125 "=",
126 DataType::Timestamp,
127 DataType::Timestamp,
128 DataType::Boolean,
129 ),
130 infix(
132 "<>",
133 DataType::Integer,
134 DataType::Integer,
135 DataType::Boolean,
136 ),
137 infix("<>", DataType::Float, DataType::Float, DataType::Boolean),
138 infix("<>", DataType::Text, DataType::Text, DataType::Boolean),
139 infix("<", DataType::Integer, DataType::Integer, DataType::Boolean),
141 infix("<", DataType::Float, DataType::Float, DataType::Boolean),
142 infix("<", DataType::Text, DataType::Text, DataType::Boolean),
143 infix(
144 "<",
145 DataType::Timestamp,
146 DataType::Timestamp,
147 DataType::Boolean,
148 ),
149 infix(
150 "<=",
151 DataType::Integer,
152 DataType::Integer,
153 DataType::Boolean,
154 ),
155 infix("<=", DataType::Float, DataType::Float, DataType::Boolean),
156 infix("<=", DataType::Text, DataType::Text, DataType::Boolean),
157 infix(">", DataType::Integer, DataType::Integer, DataType::Boolean),
158 infix(">", DataType::Float, DataType::Float, DataType::Boolean),
159 infix(">", DataType::Text, DataType::Text, DataType::Boolean),
160 infix(
161 ">=",
162 DataType::Integer,
163 DataType::Integer,
164 DataType::Boolean,
165 ),
166 infix(">=", DataType::Float, DataType::Float, DataType::Boolean),
167 infix(">=", DataType::Text, DataType::Text, DataType::Boolean),
168 infix(
170 "AND",
171 DataType::Boolean,
172 DataType::Boolean,
173 DataType::Boolean,
174 ),
175 infix(
176 "OR",
177 DataType::Boolean,
178 DataType::Boolean,
179 DataType::Boolean,
180 ),
181 prefix("NOT", DataType::Boolean, DataType::Boolean),
182];
183
184pub fn lookup(name: &str) -> Vec<&'static OperatorEntry> {
188 OPERATOR_CATALOG.iter().filter(|e| e.name == name).collect()
189}
190
191pub fn resolve(
204 name: &str,
205 kind: OperatorKind,
206 lhs: DataType,
207 rhs: DataType,
208) -> Option<&'static OperatorEntry> {
209 let candidates: Vec<&'static OperatorEntry> = OPERATOR_CATALOG
210 .iter()
211 .filter(|e| e.name == name && e.kind == kind)
212 .collect();
213
214 if candidates.is_empty() {
215 return None;
216 }
217
218 let mut best: Option<(usize, &'static OperatorEntry)> = None;
219 for entry in candidates {
220 let lhs_match = match kind {
221 OperatorKind::Infix => (entry.lhs_type == lhs) as usize,
222 OperatorKind::Prefix | OperatorKind::Postfix => 0,
223 };
224 let rhs_match = (entry.rhs_type == rhs) as usize;
225 let score = lhs_match + rhs_match;
226
227 if score == 0 && OPERATOR_CATALOG.iter().filter(|e| e.name == name).count() > 1 {
230 continue;
231 }
232
233 match best {
234 None => best = Some((score, entry)),
235 Some((prev_score, prev_entry)) => {
236 if score > prev_score
237 || (score == prev_score
238 && entry.return_type.is_preferred()
239 && !prev_entry.return_type.is_preferred())
240 {
241 best = Some((score, entry));
242 }
243 }
244 }
245 }
246
247 best.map(|(_, entry)| entry)
248}