Diffstat (limited to 'src/exec/python.rs')
| -rw-r--r-- | src/exec/python.rs | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/exec/python.rs b/src/exec/python.rs new file mode 100644 index 0000000..313d013 --- /dev/null +++ b/src/exec/python.rs @@ -0,0 +1,112 @@ +use std::ffi::CString; + +use pyo3::exceptions::PyTypeError; +use pyo3::prelude::*; +use pyo3::types::*; + +use super::{Array, Context, Error, Stack, Val}; +use crate::parser::types::Spanned; +use crate::parser::util::Spanner as _; +impl<'py> IntoPyObject<'py> for Array { + type Target = PyList; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject( + self, + p: Python<'py>, + ) -> Result<Self::Output, Self::Error> { + match self { + Self::Array(x) => PyList::new(p, x), + Self::Int(x) => PyList::new(p, x), + Self::Float(x) => PyList::new(p, x), + } + } +} + +impl<'s, 'py> IntoPyObject<'py> for Val<'s> { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject( + self, + p: Python<'py>, + ) -> Result<Self::Output, Self::Error> { + Ok(match self { + Self::Float(x) => x.into_pyobject(p).map(Bound::into_any)?, + Self::Int(x) => x.into_pyobject(p).map(Bound::into_any)?, + Self::Array(x) => x.into_pyobject(p).map(Bound::into_any)?, + Self::Set(x) => x.into_pyobject(p).map(Bound::into_any)?, + Self::Map(x) => x.into_pyobject(p).map(Bound::into_any)?, + Self::Lambda(_) => return Err(PyTypeError::new_err("λ")), + }) + } +} + +impl<'py, 's> FromPyObject<'py> for Val<'s> { + fn extract_bound(x: &Bound<'py, PyAny>) -> PyResult<Self> { + Ok(match () { + () if let Ok(x) = x.extract::<i128>() => Val::Int(x), + () if let Ok(x) = x.extract::<f64>() => Val::Float(x), + () if let Ok(x) = x.extract::<bool>() => Val::Int(x as i128), + () if let Ok(x) = x.downcast::<PySet>() => Val::Set( + x.into_iter() + .map(|x| x.extract::<Val<'s>>()) + .try_collect()?, + ), + _ => return Err(PyTypeError::new_err("bad types")), + }) + } +} + +pub fn exec<'s>( + span: super::Span, + code: Spanned<CString>, + stack: &mut Stack<'s>, + argc: super::Argc, + context: &Context<'s, '_>, +) -> super::Result<()> { + pyo3::prepare_freethreaded_python(); + Python::with_gil(|g| { + let locals = PyDict::new(g); + context + .all() + .flat_map(|x| { + x.variables.iter().map(|(x, y)| (x, y.inner.clone())) + }) + .for_each(|(k, v)| { + _ = locals.set_item(k, v); + }); + locals + .set_item( + "s", + PyList::new( + g, + stack.take(argc.input).map(Spanned::unspan), + ) + .map_err(|_| { + Error::lazy( + span, + "you must have a lambda or something", + ) + })?, + ) + .map_err(|_| Error::lazy(span, "what is wrong with python"))?; + g.run(&code, None, Some(&locals)).map_err(|x| { + x.display(g); + Error::lazy(code.span, "you wrote your 🐍 ( python) wrong") + })?; + let x = locals.get_item("s").unwrap().unwrap(); + let x = x.downcast::<PyList>().unwrap(); + let n = x.len(); + stack.extend( + x.into_iter() + .skip(n.saturating_sub(argc.output)) + .map(|x| x.extract::<Val<'_>>().map(|x| x.spun(span))) + .try_collect::<Vec<_>>() + .map_err(|_| Error::lazy(code.span, "nooo"))?, + ); + Ok(()) + }) +} |