Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-plan/src/plans/expr_ir.rs
6940 views
1
use std::borrow::Borrow;
2
use std::hash::Hash;
3
#[cfg(feature = "cse")]
4
use std::hash::Hasher;
5
use std::sync::OnceLock;
6
7
use polars_utils::format_pl_smallstr;
8
#[cfg(feature = "ir_serde")]
9
use serde::{Deserialize, Serialize};
10
11
use super::*;
12
use crate::constants::{get_len_name, get_literal_name};
13
14
#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)]
15
#[cfg_attr(feature = "ir_serde", derive(Serialize, Deserialize))]
16
pub enum OutputName {
17
/// No not yet set.
18
#[default]
19
None,
20
/// The most left-hand-side literal will be the output name.
21
LiteralLhs(PlSmallStr),
22
/// The most left-hand-side column will be the output name.
23
ColumnLhs(PlSmallStr),
24
/// Rename the output as `PlSmallStr`.
25
Alias(PlSmallStr),
26
#[cfg(feature = "dtype-struct")]
27
/// A struct field.
28
Field(PlSmallStr),
29
}
30
31
impl OutputName {
32
pub fn get(&self) -> Option<&PlSmallStr> {
33
match self {
34
OutputName::Alias(name) => Some(name),
35
OutputName::ColumnLhs(name) => Some(name),
36
OutputName::LiteralLhs(name) => Some(name),
37
#[cfg(feature = "dtype-struct")]
38
OutputName::Field(name) => Some(name),
39
OutputName::None => None,
40
}
41
}
42
43
pub fn unwrap(&self) -> &PlSmallStr {
44
self.get().expect("no output name set")
45
}
46
47
pub fn into_inner(self) -> Option<PlSmallStr> {
48
match self {
49
OutputName::Alias(name) => Some(name),
50
OutputName::ColumnLhs(name) => Some(name),
51
OutputName::LiteralLhs(name) => Some(name),
52
#[cfg(feature = "dtype-struct")]
53
OutputName::Field(name) => Some(name),
54
OutputName::None => None,
55
}
56
}
57
58
pub(crate) fn is_none(&self) -> bool {
59
matches!(self, OutputName::None)
60
}
61
}
62
63
#[derive(Clone, Debug)]
64
#[cfg_attr(feature = "ir_serde", derive(Serialize, Deserialize))]
65
pub struct ExprIR {
66
/// Output name of this expression.
67
output_name: OutputName,
68
/// Output dtype of this expression
69
/// Reduced expression.
70
/// This expression is pruned from `alias` and already expanded.
71
node: Node,
72
#[cfg_attr(feature = "ir_serde", serde(skip))]
73
output_dtype: OnceLock<DataType>,
74
}
75
76
impl Eq for ExprIR {}
77
78
impl PartialEq for ExprIR {
79
fn eq(&self, other: &Self) -> bool {
80
self.node == other.node && self.output_name == other.output_name
81
}
82
}
83
84
impl Borrow<Node> for ExprIR {
85
fn borrow(&self) -> &Node {
86
&self.node
87
}
88
}
89
90
impl Borrow<Node> for &ExprIR {
91
fn borrow(&self) -> &Node {
92
&self.node
93
}
94
}
95
96
impl ExprIR {
97
pub fn new(node: Node, output_name: OutputName) -> Self {
98
debug_assert!(!output_name.is_none());
99
ExprIR {
100
output_name,
101
node,
102
output_dtype: OnceLock::new(),
103
}
104
}
105
106
pub fn with_dtype(self, dtype: DataType) -> Self {
107
let _ = self.output_dtype.set(dtype);
108
self
109
}
110
111
pub(crate) fn set_dtype(&mut self, dtype: DataType) {
112
self.output_dtype = OnceLock::from(dtype);
113
}
114
115
pub fn from_node(node: Node, arena: &Arena<AExpr>) -> Self {
116
let mut out = Self {
117
node,
118
output_name: OutputName::None,
119
output_dtype: OnceLock::new(),
120
};
121
out.node = node;
122
for (_, ae) in arena.iter(node) {
123
match ae {
124
AExpr::Column(name) => {
125
out.output_name = OutputName::ColumnLhs(name.clone());
126
break;
127
},
128
AExpr::Literal(lv) => {
129
if let LiteralValue::Series(s) = lv {
130
out.output_name = OutputName::LiteralLhs(s.name().clone());
131
} else {
132
out.output_name = OutputName::LiteralLhs(get_literal_name().clone());
133
}
134
break;
135
},
136
AExpr::Function {
137
input, function, ..
138
} => {
139
match function {
140
#[cfg(feature = "dtype-struct")]
141
IRFunctionExpr::StructExpr(IRStructFunction::FieldByName(name)) => {
142
out.output_name = OutputName::Field(name.clone());
143
},
144
_ => {
145
if input.is_empty() {
146
out.output_name =
147
OutputName::LiteralLhs(format_pl_smallstr!("{}", function));
148
} else {
149
out.output_name = input[0].output_name.clone();
150
}
151
},
152
}
153
break;
154
},
155
AExpr::AnonymousFunction { input, fmt_str, .. } => {
156
if input.is_empty() {
157
out.output_name = OutputName::LiteralLhs(fmt_str.as_ref().clone());
158
} else {
159
out.output_name = input[0].output_name.clone();
160
}
161
break;
162
},
163
AExpr::Len => {
164
out.output_name = OutputName::LiteralLhs(get_len_name());
165
break;
166
},
167
_ => {},
168
}
169
}
170
debug_assert!(!out.output_name.is_none());
171
out
172
}
173
174
#[inline]
175
pub fn node(&self) -> Node {
176
self.node
177
}
178
179
/// Create a `ExprIR` structure that implements display
180
pub fn display<'a>(&'a self, expr_arena: &'a Arena<AExpr>) -> ExprIRDisplay<'a> {
181
ExprIRDisplay {
182
node: self.node(),
183
output_name: self.output_name_inner(),
184
expr_arena,
185
}
186
}
187
188
pub(crate) fn set_node(&mut self, node: Node) {
189
self.node = node;
190
self.output_dtype = OnceLock::new();
191
}
192
193
pub(crate) fn set_alias(&mut self, name: PlSmallStr) {
194
self.output_name = OutputName::Alias(name)
195
}
196
197
pub fn with_alias(&self, name: PlSmallStr) -> Self {
198
Self {
199
output_name: OutputName::Alias(name),
200
node: self.node,
201
output_dtype: self.output_dtype.clone(),
202
}
203
}
204
205
pub(crate) fn set_columnlhs(&mut self, name: PlSmallStr) {
206
debug_assert!(matches!(
207
self.output_name,
208
OutputName::ColumnLhs(_) | OutputName::None
209
));
210
self.output_name = OutputName::ColumnLhs(name)
211
}
212
213
pub fn output_name_inner(&self) -> &OutputName {
214
&self.output_name
215
}
216
217
pub fn output_name(&self) -> &PlSmallStr {
218
self.output_name.unwrap()
219
}
220
221
pub fn to_expr(&self, expr_arena: &Arena<AExpr>) -> Expr {
222
let out = node_to_expr(self.node, expr_arena);
223
224
match &self.output_name {
225
OutputName::Alias(name) if expr_arena.get(self.node).to_name(expr_arena) != name => {
226
out.alias(name.clone())
227
},
228
_ => out,
229
}
230
}
231
232
pub fn get_alias(&self) -> Option<&PlSmallStr> {
233
match &self.output_name {
234
OutputName::Alias(name) => Some(name),
235
_ => None,
236
}
237
}
238
239
// Utility for debugging.
240
#[cfg(debug_assertions)]
241
#[allow(dead_code)]
242
pub(crate) fn print(&self, expr_arena: &Arena<AExpr>) {
243
eprintln!("{:?}", self.to_expr(expr_arena))
244
}
245
246
pub(crate) fn has_alias(&self) -> bool {
247
matches!(self.output_name, OutputName::Alias(_))
248
}
249
250
#[cfg(feature = "cse")]
251
pub(crate) fn traverse_and_hash<H: Hasher>(&self, expr_arena: &Arena<AExpr>, state: &mut H) {
252
traverse_and_hash_aexpr(self.node, expr_arena, state);
253
if let Some(alias) = self.get_alias() {
254
alias.hash(state)
255
}
256
}
257
258
pub fn is_scalar(&self, expr_arena: &Arena<AExpr>) -> bool {
259
is_scalar_ae(self.node, expr_arena)
260
}
261
262
pub fn dtype(&self, schema: &Schema, expr_arena: &Arena<AExpr>) -> PolarsResult<&DataType> {
263
match self.output_dtype.get() {
264
Some(dtype) => Ok(dtype),
265
None => {
266
let dtype = expr_arena.get(self.node).to_dtype(schema, expr_arena)?;
267
let _ = self.output_dtype.set(dtype);
268
Ok(self.output_dtype.get().unwrap())
269
},
270
}
271
}
272
273
pub fn field(&self, schema: &Schema, expr_arena: &Arena<AExpr>) -> PolarsResult<Field> {
274
let dtype = self.dtype(schema, expr_arena)?;
275
let name = self.output_name();
276
Ok(Field::new(name.clone(), dtype.clone()))
277
}
278
279
pub fn into_inner(self) -> (Node, OutputName) {
280
(self.node, self.output_name)
281
}
282
}
283
284
impl AsRef<ExprIR> for ExprIR {
285
fn as_ref(&self) -> &ExprIR {
286
self
287
}
288
}
289
290
/// A Node that is restricted to `AExpr::Column`
291
#[repr(transparent)]
292
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
293
pub struct ColumnNode(pub(crate) Node);
294
295
impl From<ColumnNode> for Node {
296
fn from(value: ColumnNode) -> Self {
297
value.0
298
}
299
}
300
impl From<&ExprIR> for Node {
301
fn from(value: &ExprIR) -> Self {
302
value.node()
303
}
304
}
305
306
pub(crate) fn name_to_expr_ir(name: PlSmallStr, expr_arena: &mut Arena<AExpr>) -> ExprIR {
307
let node = expr_arena.add(AExpr::Column(name.clone()));
308
ExprIR::new(node, OutputName::ColumnLhs(name))
309
}
310
311
pub(crate) fn names_to_expr_irs<I, S>(names: I, expr_arena: &mut Arena<AExpr>) -> Vec<ExprIR>
312
where
313
I: IntoIterator<Item = S>,
314
S: Into<PlSmallStr>,
315
{
316
names
317
.into_iter()
318
.map(|name| {
319
let name = name.into();
320
name_to_expr_ir(name, expr_arena)
321
})
322
.collect()
323
}
324
325