use super::*;
#[cfg_attr(feature = "python_stubgen", gen_stub_pyclass_enum)]
#[pyclass(
from_py_object,
name = "AtomType",
eq,
eq_int,
module = "symbolica.core"
)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum PythonAtomType {
Num,
Var,
Fn,
Add,
Mul,
Pow,
}
#[cfg_attr(feature = "python_stubgen", gen_stub_pyclass_enum)]
#[pyclass(
from_py_object,
name = "SymbolAttribute",
eq,
eq_int,
module = "symbolica.core"
)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum PythonSymbolAttribute {
Symmetric,
Antisymmetric,
Cyclesymmetric,
Linear,
Scalar,
Real,
Integer,
Positive,
}
impl From<SymbolAttribute> for PythonSymbolAttribute {
fn from(attr: SymbolAttribute) -> Self {
match attr {
SymbolAttribute::Symmetric => PythonSymbolAttribute::Symmetric,
SymbolAttribute::Antisymmetric => PythonSymbolAttribute::Antisymmetric,
SymbolAttribute::Cyclesymmetric => PythonSymbolAttribute::Cyclesymmetric,
SymbolAttribute::Linear => PythonSymbolAttribute::Linear,
SymbolAttribute::Scalar => PythonSymbolAttribute::Scalar,
SymbolAttribute::Real => PythonSymbolAttribute::Real,
SymbolAttribute::Integer => PythonSymbolAttribute::Integer,
SymbolAttribute::Positive => PythonSymbolAttribute::Positive,
}
}
}
#[derive(Clone)]
#[cfg_attr(feature = "python_stubgen", gen_stub_pyclass)]
#[pyclass(from_py_object, name = "AtomTree", module = "symbolica.core")]
pub struct PythonAtomTree {
#[pyo3(get)]
pub atom_type: PythonAtomType,
#[pyo3(get)]
pub head: Option<String>,
#[pyo3(get)]
pub tail: Vec<PythonAtomTree>,
}
impl<'a> From<AtomView<'a>> for PyResult<PythonAtomTree> {
fn from(atom: AtomView<'a>) -> Self {
let tree = match atom {
AtomView::Num(_) => PythonAtomTree {
atom_type: PythonAtomType::Num,
head: Some(format!("{}", AtomPrinter::new(atom))),
tail: vec![],
},
AtomView::Var(v) => PythonAtomTree {
atom_type: PythonAtomType::Var,
head: Some(v.get_symbol().get_name().to_string()),
tail: vec![],
},
AtomView::Fun(f) => PythonAtomTree {
atom_type: PythonAtomType::Fn,
head: Some(f.get_symbol().get_name().to_string()),
tail: f.iter().map(|x| x.into()).collect::<Result<Vec<_>, _>>()?,
},
AtomView::Add(a) => PythonAtomTree {
atom_type: PythonAtomType::Add,
head: None,
tail: a.iter().map(|x| x.into()).collect::<Result<Vec<_>, _>>()?,
},
AtomView::Mul(m) => PythonAtomTree {
atom_type: PythonAtomType::Mul,
head: None,
tail: m.iter().map(|x| x.into()).collect::<Result<Vec<_>, _>>()?,
},
AtomView::Pow(p) => {
let (b, e) = p.get_base_exp();
PythonAtomTree {
atom_type: PythonAtomType::Pow,
head: None,
tail: vec![
<AtomView as Into<PyResult<PythonAtomTree>>>::into(b)?,
<AtomView as Into<PyResult<PythonAtomTree>>>::into(e)?,
],
}
}
};
Ok(tree)
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct PythonUserDataKey(pub(super) UserDataKey);
impl<'py> IntoPyObject<'py> for &PythonUserDataKey {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
match &self.0 {
UserDataKey::Integer(i) => i.into_bound_py_any(py),
UserDataKey::Atom(a) => {
let expr: PythonExpression = a.clone().into();
expr.into_bound_py_any(py)
}
UserDataKey::String(s) => s.into_bound_py_any(py),
}
}
}
impl<'py> FromPyObject<'_, 'py> for PythonUserDataKey {
type Error = PyErr;
fn extract(ob: Borrowed<'_, 'py, pyo3::PyAny>) -> PyResult<Self> {
if let Ok(num) = ob.extract::<i64>() {
Ok(PythonUserDataKey(UserDataKey::Integer(num)))
} else if let Ok(a) = ob.extract::<ConvertibleToExpression>() {
Ok(PythonUserDataKey(UserDataKey::Atom(a.to_expression().expr)))
} else if let Ok(s) = ob.extract::<PyBackedStr>() {
Ok(PythonUserDataKey(UserDataKey::String(s.to_string())))
} else {
Err(exceptions::PyTypeError::new_err(
"Cannot convert to ExtendedUserDataKey",
))
}
}
}
pub struct PythonUserData(pub(super) UserData);
#[cfg(feature = "python_stubgen")]
impl_stub_type!(PythonUserData = ConvertibleToExpression | PyBackedStr | PyDict | PyList | PyBytes);
impl<'py> FromPyObject<'_, 'py> for PythonUserData {
type Error = PyErr;
fn extract(ob: Borrowed<'_, 'py, pyo3::PyAny>) -> PyResult<Self> {
if ob.extract::<Py<PyNone>>().is_ok() {
Ok(PythonUserData(UserData::None))
} else if let Ok(num) = ob.extract::<i64>() {
Ok(PythonUserData(UserData::Integer(num)))
} else if let Ok(a) = ob.extract::<ConvertibleToExpression>() {
Ok(PythonUserData(UserData::Atom(a.to_expression().expr)))
} else if let Ok(s) = ob.extract::<PyBackedStr>() {
Ok(PythonUserData(UserData::String(s.to_string())))
} else if let Ok(list) = ob.extract::<Vec<PythonUserData>>() {
Ok(PythonUserData(UserData::List(
list.into_iter().map(|x| x.0).collect(),
)))
} else if let Ok(map) = ob.extract::<HashMap<PythonUserDataKey, PythonUserData>>() {
Ok(PythonUserData(UserData::Map(
map.into_iter().map(|(k, v)| (k.0, v.0)).collect(),
)))
} else if let Ok(bytes) = ob.extract::<&[u8]>() {
Ok(PythonUserData(UserData::Serialized(bytes.to_vec())))
} else {
Err(exceptions::PyTypeError::new_err(
"Cannot convert to ExtendedUserData",
))
}
}
}
pub(super) struct PythonBorrowedUserData<'a>(pub(super) &'a UserData);
impl<'a, 'py> IntoPyObject<'py> for PythonBorrowedUserData<'a> {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
match self {
PythonBorrowedUserData(UserData::None) => PyNone::get(py).into_bound_py_any(py),
PythonBorrowedUserData(UserData::Integer(i)) => i.into_bound_py_any(py),
PythonBorrowedUserData(UserData::Atom(a)) => {
let expr: PythonExpression = a.clone().into();
expr.into_bound_py_any(py)
}
PythonBorrowedUserData(UserData::String(s)) => s.into_bound_py_any(py),
PythonBorrowedUserData(UserData::List(l)) => {
let pl: Vec<PythonBorrowedUserData> =
l.into_iter().map(|x| PythonBorrowedUserData(x)).collect();
pl.into_bound_py_any(py)
}
PythonBorrowedUserData(UserData::Map(m)) => {
let pm: HashMap<_, _> = m
.into_iter()
.map(|(k, v)| {
Ok((
PythonUserDataKey(k.clone()),
PythonBorrowedUserData(v).into_pyobject(py)?,
))
})
.collect::<Result<_, PyErr>>()?;
pm.into_bound_py_any(py)
}
PythonBorrowedUserData(UserData::Serialized(b)) => b.into_bound_py_any(py),
}
}
}
#[derive(FromPyObject)]
pub enum ConvertibleToPattern {
Literal(ConvertibleToExpression),
Held(PythonHeldExpression),
}
#[cfg(feature = "python_stubgen")]
impl_stub_type!(ConvertibleToPattern = ConvertibleToExpression | PythonHeldExpression);
impl ConvertibleToPattern {
pub fn to_pattern(self) -> PyResult<PythonHeldExpression> {
match self {
Self::Literal(l) => Ok(l.to_expression().expr.to_pattern().into()),
Self::Held(e) => Ok(e),
}
}
}
#[derive(FromPyObject)]
pub enum ConvertibleToOpenPattern {
Closed(ConvertibleToPattern),
Open(PythonTransformer),
}
#[cfg(feature = "python_stubgen")]
impl_stub_type!(ConvertibleToOpenPattern = ConvertibleToPattern | PythonTransformer);
impl ConvertibleToOpenPattern {
pub fn to_pattern(self) -> PyResult<PythonHeldExpression> {
match self {
Self::Closed(l) => l.to_pattern(),
Self::Open(e) => Ok(Pattern::Transformer(Box::new((None, e.chain))).into()),
}
}
}
#[derive(FromPyObject)]
pub enum ConvertibleToReplaceWith {
Pattern(ConvertibleToPattern),
Map(Py<PyAny>),
}
#[cfg(feature = "python_stubgen")]
pub struct ReplaceFunction;
#[cfg(feature = "python_stubgen")]
impl PyStubType for ReplaceFunction {
fn type_output() -> TypeInfo {
TypeInfo {
name: "typing.Callable[[dict[Expression, Expression]], Expression] | int | float | complex | decimal.Decimal".into(),
import: {
let mut h = std::collections::HashSet::default();
h.insert("decimal".into());
h
},
}
}
}
#[cfg(feature = "python_stubgen")]
impl_stub_type!(ConvertibleToReplaceWith = ConvertibleToPattern | ReplaceFunction);
impl ConvertibleToReplaceWith {
pub fn to_replace_with(self) -> PyResult<ReplaceWith<'static>> {
match self {
Self::Pattern(p) => Ok(ReplaceWith::Pattern(p.to_pattern()?.expr.into())),
Self::Map(m) => Ok(ReplaceWith::Map(Box::new(move |match_stack| {
let match_stack: HashMap<PythonExpression, PythonExpression> = match_stack
.get_matches()
.iter()
.map(|x| (Atom::var(x.0).into(), x.1.to_atom().into()))
.collect();
Python::attach(|py| {
m.call(py, (match_stack,), None)
.map_err(|e| {
error!("Python match map callback failed: {}", e);
})
.and_then(|x| {
x.extract::<PythonExpression>(py).map_err(|e| {
error!("Python match map callback failed: {}", e);
})
})
.map(|x| x.expr)
.unwrap_or_else(|_| Atom::num(Coefficient::Indeterminate))
})
}))),
}
}
}
#[derive(FromPyObject)]
pub enum OneOrMultiple<T> {
One(T),
Multiple(Vec<T>),
}
impl<T> OneOrMultiple<T> {
pub fn to_iter(&self) -> impl Iterator<Item = &T> {
match self {
OneOrMultiple::One(a) => std::slice::from_ref(a).iter(),
OneOrMultiple::Multiple(m) => m.iter(),
}
}
}