use std::borrow::Borrow;
use std::hash::Hash;
#[cfg(feature = "cse")]
use std::hash::Hasher;
use polars_utils::format_pl_smallstr;
#[cfg(feature = "ir_serde")]
use serde::{Deserialize, Serialize};
use super::*;
use crate::constants::{get_len_name, get_literal_name};
#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "ir_serde", derive(Serialize, Deserialize))]
pub enum OutputName {
#[default]
None,
LiteralLhs(PlSmallStr),
ColumnLhs(PlSmallStr),
Alias(PlSmallStr),
#[cfg(feature = "dtype-struct")]
Field(PlSmallStr),
}
impl OutputName {
pub fn get(&self) -> Option<&PlSmallStr> {
match self {
OutputName::Alias(name) => Some(name),
OutputName::ColumnLhs(name) => Some(name),
OutputName::LiteralLhs(name) => Some(name),
#[cfg(feature = "dtype-struct")]
OutputName::Field(name) => Some(name),
OutputName::None => None,
}
}
pub fn unwrap(&self) -> &PlSmallStr {
self.get().expect("no output name set")
}
pub(crate) fn is_none(&self) -> bool {
matches!(self, OutputName::None)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "ir_serde", derive(Serialize, Deserialize))]
pub struct ExprIR {
output_name: OutputName,
node: Node,
}
impl Borrow<Node> for ExprIR {
fn borrow(&self) -> &Node {
&self.node
}
}
impl ExprIR {
pub fn new(node: Node, output_name: OutputName) -> Self {
debug_assert!(!output_name.is_none());
ExprIR { output_name, node }
}
pub fn from_node(node: Node, arena: &Arena<AExpr>) -> Self {
let mut out = Self {
node,
output_name: OutputName::None,
};
out.node = node;
for (_, ae) in arena.iter(node) {
match ae {
AExpr::Column(name) => {
out.output_name = OutputName::ColumnLhs(name.clone());
break;
},
AExpr::Literal(lv) => {
if let LiteralValue::Series(s) = lv {
out.output_name = OutputName::LiteralLhs(s.name().clone());
} else {
out.output_name = OutputName::LiteralLhs(get_literal_name().clone());
}
break;
},
AExpr::Function {
input, function, ..
} => {
match function {
#[cfg(feature = "dtype-struct")]
FunctionExpr::StructExpr(StructFunction::FieldByName(name)) => {
out.output_name = OutputName::Field(name.clone());
},
_ => {
if input.is_empty() {
out.output_name =
OutputName::LiteralLhs(format_pl_smallstr!("{}", function));
} else {
out.output_name = input[0].output_name.clone();
}
},
}
break;
},
AExpr::AnonymousFunction { input, options, .. } => {
if input.is_empty() {
out.output_name =
OutputName::LiteralLhs(PlSmallStr::from_static(options.fmt_str));
} else {
out.output_name = input[0].output_name.clone();
}
break;
},
AExpr::Len => out.output_name = OutputName::LiteralLhs(get_len_name()),
AExpr::Alias(_, _) => {
#[cfg(debug_assertions)]
{
unreachable!()
}
},
_ => {},
}
}
debug_assert!(!out.output_name.is_none());
out
}
#[inline]
pub fn node(&self) -> Node {
self.node
}
pub fn display<'a>(&'a self, expr_arena: &'a Arena<AExpr>) -> ExprIRDisplay<'a> {
ExprIRDisplay {
node: self.node(),
output_name: self.output_name_inner(),
expr_arena,
}
}
pub(crate) fn set_node(&mut self, node: Node) {
self.node = node;
}
#[cfg(feature = "cse")]
pub(crate) fn set_alias(&mut self, name: PlSmallStr) {
self.output_name = OutputName::Alias(name)
}
pub(crate) fn set_columnlhs(&mut self, name: PlSmallStr) {
self.output_name = OutputName::ColumnLhs(name)
}
pub fn output_name_inner(&self) -> &OutputName {
&self.output_name
}
pub fn output_name(&self) -> &PlSmallStr {
self.output_name.unwrap()
}
pub fn to_expr(&self, expr_arena: &Arena<AExpr>) -> Expr {
let out = node_to_expr(self.node, expr_arena);
match &self.output_name {
OutputName::Alias(name) => out.alias(name.clone()),
_ => out,
}
}
pub fn get_alias(&self) -> Option<&PlSmallStr> {
match &self.output_name {
OutputName::Alias(name) => Some(name),
_ => None,
}
}
pub(crate) fn get_non_projected_name(&self) -> Option<&PlSmallStr> {
match &self.output_name {
OutputName::Alias(name) => Some(name),
#[cfg(feature = "dtype-struct")]
OutputName::Field(name) => Some(name),
OutputName::LiteralLhs(name) => Some(name),
_ => None,
}
}
#[cfg(debug_assertions)]
#[allow(dead_code)]
pub(crate) fn print(&self, expr_arena: &Arena<AExpr>) {
eprintln!("{:?}", self.to_expr(expr_arena))
}
pub(crate) fn has_alias(&self) -> bool {
matches!(self.output_name, OutputName::Alias(_))
}
#[cfg(feature = "cse")]
pub(crate) fn traverse_and_hash<H: Hasher>(&self, expr_arena: &Arena<AExpr>, state: &mut H) {
traverse_and_hash_aexpr(self.node, expr_arena, state);
if let Some(alias) = self.get_alias() {
alias.hash(state)
}
}
pub fn is_scalar(&self, expr_arena: &Arena<AExpr>) -> bool {
is_scalar_ae(self.node, expr_arena)
}
}
impl AsRef<ExprIR> for ExprIR {
fn as_ref(&self) -> &ExprIR {
self
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ColumnNode(pub(crate) Node);
impl From<ColumnNode> for Node {
fn from(value: ColumnNode) -> Self {
value.0
}
}
impl From<&ExprIR> for Node {
fn from(value: &ExprIR) -> Self {
value.node()
}
}
pub(crate) fn name_to_expr_ir(name: PlSmallStr, expr_arena: &mut Arena<AExpr>) -> ExprIR {
let node = expr_arena.add(AExpr::Column(name.clone()));
ExprIR::new(node, OutputName::ColumnLhs(name))
}
pub(crate) fn names_to_expr_irs<I, S>(names: I, expr_arena: &mut Arena<AExpr>) -> Vec<ExprIR>
where
I: IntoIterator<Item = S>,
S: Into<PlSmallStr>,
{
names
.into_iter()
.map(|name| {
let name = name.into();
name_to_expr_ir(name, expr_arena)
})
.collect()
}