add python
| -rw-r--r-- | Cargo.lock | 139 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/exec.rs | 99 | ||||
| -rw-r--r-- | src/exec/python.rs | 112 | ||||
| -rw-r--r-- | src/lexer.rs | 7 | ||||
| -rw-r--r-- | src/main.rs | 2 | ||||
| -rw-r--r-- | src/parser.rs | 2 | ||||
| -rw-r--r-- | src/parser/fun.rs | 12 | ||||
| -rw-r--r-- | src/parser/types.rs | 13 |
9 files changed, 342 insertions, 45 deletions
@@ -44,12 +44,24 @@ dependencies = [ ] [[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] name = "beef" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] name = "chumsky" version = "1.0.0-alpha.8" source = "git+https://github.com/zesterer/chumsky#d12869af1701647e0f0034aef1cce14fa2e170be" @@ -76,7 +88,7 @@ checksum = "f24f01cb5d7f027b65718cfab34b16ae783a3290d9b16e15d7e4310e0c24c173" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.99", ] [[package]] @@ -115,6 +127,18 @@ dependencies = [ ] [[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] name = "itertools" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -136,6 +160,7 @@ dependencies = [ "logos", "match_deref", "paste", + "pyo3", "regex", "tinyvec", "umath", @@ -159,6 +184,12 @@ dependencies = [ ] [[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] name = "logos" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -180,7 +211,7 @@ dependencies = [ "quote", "regex-syntax", "rustc_version", - "syn 2.0.48", + "syn 2.0.99", ] [[package]] @@ -210,21 +241,105 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] +name = "pyo3" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.99", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.99", +] + +[[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -290,9 +405,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -300,6 +415,12 @@ dependencies = [ ] [[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -348,6 +469,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -20,3 +20,4 @@ regex = "1.11.1" itertools = "0.14.0" codespan-reporting = { git = "https://github.com/brendanzab/codespan", version = "0.11.1" } umath = "0.0.7" +pyo3 = "0.23.5" diff --git a/src/exec.rs b/src/exec.rs index 0ede0b4..64f84eb 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,10 +1,11 @@ use std::collections::{BTreeSet, HashMap, HashSet}; +use std::ffi::CString; use std::fmt::Display; use std::hash::Hash; -use std::iter::once; +use std::iter::{once, successors}; use std::mem::take; use std::ops::{Add, Deref, DerefMut}; - +mod python; use chumsky::span::{SimpleSpan, Span as _}; use crate::parser::fun::Function; @@ -98,6 +99,11 @@ impl Array { fn len(&self) -> usize { each!(self, |x| x.len(), &Vec<_> => usize) } + fn windows(self, size: usize) -> Array { + each!(self, |x| Array::Array( + x.windows(size).map(|x| Array::from(x.to_vec())).collect(), + ), Vec<_> => Array) + } fn remove(&mut self, n: usize) { each!(self, |x| { x.remove(n); }, &mut Vec<_> => ()) } @@ -119,9 +125,9 @@ impl Array { impl std::fmt::Debug for Array { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Array(x) => x.fmt(f), - Self::Int(x) => x.fmt(f), - Self::Float(x) => x.fmt(f), + Self::Array(x) => write!(f, "a{x:?}"), + Self::Int(x) => write!(f, "i{x:?}"), + Self::Float(x) => write!(f, "f{x:?}"), } } } @@ -279,6 +285,25 @@ impl<'s> Val<'s> { x => Err(Error::ef(span, "array", x.ty().spun(self.span))), } } + fn assert_utf8( + self: Spanned<Val<'s>>, + span: Span, + ) -> Result<Spanned<String>> { + self.assert_array(span)?.try_map(|x, s| match x { + Array::Int(x) => { + String::from_utf8(x.into_iter().map(|x| x as u8).collect()) + .map_err(|e| { + Error::ef( + span, + "valid utf8", + "invalid utf8".spun(s), + ) + .note(e.utf8_error().to_string()) + }) + } + x => Err(Error::ef(span, "array", x.ty().spun(s))), + }) + } fn assert_map( self: Spanned<Val<'s>>, span: Span, @@ -366,6 +391,9 @@ impl<'s, 'v> Default for Context<'s, 'v> { } impl<'s, 'v> Context<'s, 'v> { + fn all(&self) -> impl Iterator<Item = &Self> { + successors(Some(&*self), |x| x.inherits) + } fn inherits(x: &'v Context<'s, 'v>) -> Self { Self { inherits: Some(x), @@ -401,9 +429,9 @@ impl<'s> Stack<'s> { fn take( &mut self, take: usize, - ) -> impl Iterator<Item = Spanned<Val<'s>>> { + ) -> impl Iterator<Item = Spanned<Val<'s>>> + ExactSizeIterator { let n = self.len(); - self.drain(n - take..) + self.drain(n.saturating_sub(take)..) } pub fn of(x: impl Iterator<Item = Spanned<Val<'s>>>) -> Self { Self(vec![x.collect()]) @@ -553,18 +581,19 @@ impl Add<Argc> for Argc { fn size_fn<'s>(f: &Function<'s>) -> Argc { use Function::*; match f { - IndexHashMap | HashMap | Append | Length | Del | Fold(_) - | Mask | Group | Index | Sub | Add | Mul | Div | Xor | Mod - | Pow | Eq | Ne | BitAnd | Or | Ge | Le | Lt | Gt => { + Matches | Windows | IndexHashMap | HashMap | Append | Del + | Fold(_) | Mask | Group | Index | Sub | Add | Mul | Div | Xor + | Mod | Pow | Eq | Ne | BitAnd | Or | Ge | Le | Lt | Gt => { Argc::takes(2).into(1) } + Python(x) => *x, &Take(x) => Argc::takes(x as _).into(x as _), With(x) => Argc::takes(1).into(x.argc().output), Map(x) => { Argc::takes(1 + (x.argc().input.saturating_sub(1))).into(1) } - Identity | Setify | Sort | Range | Reduce(_) | Open | Neg - | Sqrt | Not => Argc::takes(1).into(1), + Length | Identity | Setify | Sort | Range | Reduce(_) | Open + | Neg | Sqrt | Not => Argc::takes(1).into(1), Flip => Argc::takes(2).into(2), Dup => Argc::takes(1).into(2), Zap(None) | Zap(Some(0)) => Argc::takes(1).into(0), @@ -581,7 +610,7 @@ fn size_fn<'s>(f: &Function<'s>) -> Argc { Both(x, n) => { Argc::takes(x.argc().input * n).into(x.argc().output * n) } - Ident(x) => Argc::takes(0).into(1), + Ident(_) => Argc::takes(0).into(1), EmptySet => Argc::takes(0).into(1), _ => Argc { input: 0, @@ -623,7 +652,7 @@ fn exec_lambda<'s>( match elem { Expr::Function(x) => match x { Function::Ident(x) => { - let (x, span) = std::iter::successors(Some(&*c), |x| x.inherits) + let (x, span) = c.all() .find_map(|c| c.variables.get(x)) .unwrap_or_else(|| { println!("couldnt find definition for variable {x} at ast node {x:?}"); @@ -1043,22 +1072,7 @@ impl<'s> Function<'s> { stack.push(out.spun(span)); } Self::Open => { - let x = pop!().assert_array(span)?.try_map( - |x, s| match x { - Array::Int(x) => String::from_utf8( - x.into_iter().map(|x| x as u8).collect(), - ) - .map_err(|e| { - Error::ef( - span, - "valid utf8", - "invalid utf8".spun(s), - ) - .note(e.utf8_error().to_string()) - }), - x => Err(Error::ef(span, "array", x.ty().spun(s))), - }, - )?; + let x = pop!().assert_utf8(span)?; stack.push( Val::Array(Array::Int( std::fs::read(&*x) @@ -1253,7 +1267,7 @@ impl<'s> Function<'s> { } } Self::Length => { - let x = stack.last().ok_or(Error::stack_empty(span))?; + let x = pop!(); stack.push( Val::Int(match &x.inner { Val::Array(x) => x.len(), @@ -1292,6 +1306,29 @@ impl<'s> Function<'s> { .spun(span), ); } + Self::Windows => { + let size = pop!().assert_int(span)?; + let array = pop!().assert_array(span)?; + stack.push(array.map(|x| { + Val::Array(x.windows(size.inner.try_into().unwrap())) + })); + } + Self::Matches => { + let a = pop!().assert_array(span)?; + let b = pop!().assert_array(span)?; + stack.push(Val::Int((a == b) as i128).spun(span)); + } + Self::Python(x) => { + let input = pop!() + .assert_utf8(span)? + .try_map(|x, _| { + let mut x = x.into_bytes(); + x.push(0); + CString::from_vec_with_nul(x) + }) + .map_err(|_| Error::lazy(span, "nooo has nul"))?; + python::exec(span, input, stack, x, c)?; + } _ => (), } Ok(()) 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(()) + }) +} diff --git a/src/lexer.rs b/src/lexer.rs index ca025fb..f411db7 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -71,11 +71,12 @@ tokens! { "→" => Place, "≡" => Eq, + "≣" => Matches, "≢" => Ne, "+" => Add, "-" => Sub, "×" => Mul, - "*" => Pow, + "ⁿ" => Pow, "<" => Lt, ">" => Gt, "≤" => Le, @@ -113,7 +114,6 @@ tokens! { "≣#️⃣" => Get, "∅" => Set, "💽" => Append, - "🚧" => Split, "⬅️" => First, "➡" => Last, "↘️" => Reduce, @@ -122,9 +122,10 @@ tokens! { "🐋" => If, "🐬" => EagerIf, "🇳🇿" => Zip, + "🪟" => Windows, "🧐" => Debug, "." => Identity, - + "🐍" => Python, } pub fn lex(s: &str) -> Lexer { diff --git a/src/main.rs b/src/main.rs index d3c8336..e67dc7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ #![feature( let_chains, try_trait_v2, + if_let_guard, iter_intersperse, iterator_try_reduce, formatting_options, + iterator_try_collect, impl_trait_in_bindings, arbitrary_self_types )] diff --git a/src/parser.rs b/src/parser.rs index 76680d1..2522444 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -33,7 +33,7 @@ impl<'s> Expr<'s> { let λ = Λ::parse(expr.clone()); choice(( inline_expr, - Function::parse(λ.clone().map(Spanned::unspan())) + Function::parse(λ.clone().map(Spanned::unspan)) .map(Expr::Function) .map_with(spanned!()), λ.map(|x| x.map(|x| Expr::Value(Value::Lambda(x)))), diff --git a/src/parser/fun.rs b/src/parser/fun.rs index bdd750c..f7ae826 100644 --- a/src/parser/fun.rs +++ b/src/parser/fun.rs @@ -3,6 +3,7 @@ use chumsky::prelude::*; use super::types::*; use super::util::*; +use crate::exec::Argc; use crate::lexer::Token; #[derive(Debug, Clone)] @@ -16,6 +17,8 @@ pub enum Function<'s> { Map(Spanned<Λ<'s>>), Dup, Flip, + Python(Argc), + Matches, Eq, Reverse, Zap(Option<u64>), @@ -26,6 +29,7 @@ pub enum Function<'s> { IndexHashMap, Not, Mul, + Windows, Pow, Type, Ne, @@ -95,6 +99,7 @@ impl<'s> Function<'s> { Token::HashMap => HashMap, Token::Get => IndexHashMap, Token::Sub => Sub, + Token::Windows => Windows, Token::Mul => Mul, Token::Pow => Pow, Token::Sqrt => Sqrt, @@ -122,10 +127,10 @@ impl<'s> Function<'s> { Token::Mod => Mod, Token::Open => Open, Token::Mask => Mask, - Token::Split => Split, Token::First => First, Token::Ne => Ne, Token::Type => Type, + Token::Matches => Matches, Token::Last => Last, Token::Ident(x) => Ident(x), } @@ -197,6 +202,11 @@ impl<'s> Function<'s> { one![With], just(Token::Zap).ignore_then(t![int]).map(Some).map(Zap), t!['['].ignore_then(t![int]).map(Take), + just(Token::Python) + .ignore_then(t![int]) + .then_ignore(t![->]) + .then(t![int]) + .map(|(a, b)| Python(Argc::takes(a as _).into(b as _))), choice(( just(Token::ArrayN) .ignore_then(t![int].map(|x| Array(Some(x)))), diff --git a/src/parser/types.rs b/src/parser/types.rs index 6a2eed9..2d93577 100644 --- a/src/parser/types.rs +++ b/src/parser/types.rs @@ -110,11 +110,18 @@ pub enum Expr<'s> { Value(Value<'s>), } -#[derive(Clone)] +#[derive(Clone, Hash)] pub struct Spanned<T> { pub(crate) span: SimpleSpan, pub inner: T, } + +impl<T: PartialEq> PartialEq for Spanned<T> { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } +} +impl<T: PartialEq> Eq for Spanned<T> {} impl<T: std::fmt::Debug> std::fmt::Debug for Spanned<T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.inner.fmt(f) @@ -156,8 +163,8 @@ impl<T> Spanned<T> { f(inner, span).map(|x| x.spun(span)) } - pub fn unspan() -> impl Fn(Spanned<T>) -> T + Copy { - |x| x.inner + pub fn unspan(self) -> T { + self.inner } pub fn span(&self) -> SimpleSpan { |