add python
bendn 12 months ago
parent dcd99f5 · commit 41115a8
-rw-r--r--Cargo.lock139
-rw-r--r--Cargo.toml1
-rw-r--r--src/exec.rs99
-rw-r--r--src/exec/python.rs112
-rw-r--r--src/lexer.rs7
-rw-r--r--src/main.rs2
-rw-r--r--src/parser.rs2
-rw-r--r--src/parser/fun.rs12
-rw-r--r--src/parser/types.rs13
9 files changed, 342 insertions, 45 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 18041dc..9b837f9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 6032259..8a7cc97 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 {