From a26cf303c8862ec92302da310665db98c160b1d6 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Fri, 20 Oct 2023 22:08:19 -0700 Subject: [PATCH 01/23] micro-optimize len-1 chain eval --- src/eval.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/eval.rs b/src/eval.rs index 34c6a2e..7fdf08b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -620,6 +620,30 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } } Ok(Obj::Func(Func::ChainSection(v1, acc), Precedence::zero())) + } else if ops.len() == 1 { + // micro-optimization, but this path is extremely common + let lhs = evaluate(env, op1)?; + let (oper, opd) = &ops[0]; + let oprr = evaluate(env, oper)?; + if let Obj::Func(b, _prec) = &oprr { + let oprd = evaluate(env, opd)?; + add_trace( + b.run(env, vec![lhs, oprd]), + format!("chain {}", oprr), + oper.start, + oper.end, + ) + } else { + Err(NErr::type_error_loc( + format!( + "Chain cannot use nonblock in operator position: {}", + FmtObj::debug(&oprr) + ), + "op 1/1".to_string(), + oper.start, + oper.end, + )) + } } else { let mut ev = ChainEvaluator::new(evaluate(env, op1)?); for (i, (oper, opd)) in ops.iter().enumerate() { From 37546219e1a149bb2eef860a9e7c8e4f421307b1 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Fri, 20 Oct 2023 22:52:59 -0700 Subject: [PATCH 02/23] profile release debug --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 050f064..4343a26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,6 @@ getrandom = { version = "0.2", features = ["js"] } # ??? [lib] crate-type = ["cdylib", "rlib"] + +[profile.release] +debug = 1 From e1a81032e2779eb4a4884d118a52c0c06e825d55 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Sat, 21 Oct 2023 02:46:05 -0700 Subject: [PATCH 03/23] Fold some constants + Box BigRational BigRational is rare and noticeably slows things down by making Obj's struct size large --- src/core.rs | 82 ++++++++++++++++++++++++++++++++++++++++++----------- src/eval.rs | 22 ++++++++------ src/lib.rs | 9 ++---- src/main.rs | 6 +++- src/nnum.rs | 41 ++++++++++++++++++--------- 5 files changed, 114 insertions(+), 46 deletions(-) diff --git a/src/core.rs b/src/core.rs index 5e6484b..cfa5d3d 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1812,6 +1812,22 @@ pub enum Expr { Splat(Box), } +impl Expr { + fn constant_value(&self) -> Option { + match self { + Expr::Null => Some(Obj::Null), + Expr::IntLit(x) => Some(Obj::from(x.clone())), + Expr::RatLit(x) => Some(Obj::from(NNum::from(x.clone()))), + Expr::FloatLit(x) => Some(Obj::from(*x)), + Expr::ImaginaryFloatLit(x) => Some(Obj::Num(NNum::Complex(Complex64::new(0.0, *x)))), + Expr::StringLit(s) => Some(Obj::Seq(Seq::String(Rc::clone(s)))), + Expr::BytesLit(s) => Some(Obj::Seq(Seq::Bytes(Rc::clone(s)))), + Expr::Frozen(x) => Some(x.clone()), + _ => None, + } + } +} + #[derive(Debug)] pub enum Lvalue { Underscore, @@ -2100,8 +2116,8 @@ pub struct Parser { i: usize, } -// This is probably eventually going to shed the word "freeze" and become a visitor pattern. -// If it hasn't already... +// This originally implemented the "freeze" keyword but is basically just a visitor pattern at this +// point. Some constant optimizations occur. #[derive(Clone)] pub struct FreezeEnv { pub bound: HashSet, @@ -2307,12 +2323,19 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { } } } - Expr::Underscore => Err(NErr::syntax_error_loc( - "Can't freeze underscore".to_string(), - "_".to_string(), - expr.start, - expr.end, - )), + Expr::Underscore => { + if env.warn { + eprintln!("\x1b[1;33mWARNING\x1b[0;33m: underscore??\x1b[0m",); + Ok(Expr::Underscore) + } else { + Err(NErr::syntax_error_loc( + "Can't freeze underscore".to_string(), + "_".to_string(), + expr.start, + expr.end, + )) + } + } Expr::Index(x, ios) => Ok(Expr::Index( box_freeze_underscore_ok(env, x)?, freeze_ios(env, ios)?, @@ -2353,14 +2376,40 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { box_freeze(env, op)?, box_freeze(env, rhs)?, )), - Expr::Call(f, args, syntax) => Ok(Expr::Call( - box_freeze_underscore_ok(env, f)?, - vec_box_freeze_underscore_ok(env, args)?, - *syntax, - )), + Expr::Call(f, args, syntax) => { + let f = box_freeze_underscore_ok(env, f)?; + let args = vec_box_freeze_underscore_ok(env, args)?; + let opt_result = match f.expr.constant_value() { + Some(Obj::Func(Func::Builtin(b), _)) => { + if b.builtin_name() == "-" && args.len() == 1 { + match args[0].expr.constant_value() { + Some(Obj::Num(x)) => Some(Obj::from(-x)), + _ => None, + } + } else { + None + } + } + _ => None, + }; + match opt_result { + Some(x) => Ok(Expr::Frozen(x)), + None => Ok(Expr::Call(f, args, *syntax)), + } + } Expr::CommaSeq(s) => Ok(Expr::CommaSeq(vec_box_freeze(env, s)?)), Expr::Splat(s) => Ok(Expr::Splat(box_freeze(env, s)?)), - Expr::List(xs) => Ok(Expr::List(vec_box_freeze_underscore_ok(env, xs)?)), + Expr::List(xs) => { + let v = vec_box_freeze_underscore_ok(env, xs)?; + match v + .iter() + .map(|e| e.expr.constant_value()) + .collect::>>() + { + Some(xs) => Ok(Expr::Frozen(Obj::list(xs))), + None => Ok(Expr::List(v)), + } + } Expr::Vector(xs) => Ok(Expr::Vector(vec_box_freeze(env, xs)?)), Expr::Dict(def, xs) => Ok(Expr::Dict( opt_box_freeze(env, def)?, @@ -3877,10 +3926,11 @@ pub enum Func { ListSection(Vec>), IndexSection(Option>, Option>), // outer None is nothing; Some(None) is a slot + // unusual boxing because bytes SliceSection( Option>, - Option>>, - Option>>, + Option>>, + Option>>, ), ChainSection( Option>, diff --git a/src/eval.rs b/src/eval.rs index 7fdf08b..740a3cf 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -548,16 +548,16 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Some(LocExpr { expr: Expr::Underscore, .. - }) => Some(None), // section slot - Some(e) => Some(Some(Box::new(evaluate(env, e)?))), + }) => Some(Box::new(None)), // section slot + Some(e) => Some(Box::new(Some(evaluate(env, e)?))), }; let jj = match j.as_deref() { None => None, // actually nothing Some(LocExpr { expr: Expr::Underscore, .. - }) => Some(None), // section slot - Some(e) => Some(Some(Box::new(evaluate(env, e)?))), + }) => Some(Box::new(None)), // section slot + Some(e) => Some(Box::new(Some(evaluate(env, e)?))), }; Ok(Obj::Func( Func::SliceSection(None, ii, jj), @@ -1390,7 +1390,7 @@ pub fn index(xr: Obj, ir: Obj) -> NRes { }, }, (Obj::Func(_, Precedence(p, _)), Obj::Seq(Seq::String(k))) => match &**k { - "precedence" => Ok(Obj::from(*p)), + "precedence" => Ok(Obj::from(*p as f64)), _ => Err(NErr::type_error(format!("can't index into func {:?}", k))), }, (Obj::Instance(struc, fields), Obj::Func(Func::StructField(istruc, field_index), _)) => { @@ -2451,13 +2451,17 @@ impl Func { }; let lo = match lo { None => None, - Some(None) => Some(it.next().ok_or(NErr::argument_error("index section: too few arguments".to_string()))?), - Some(Some(e)) => Some((**e).clone()), + Some(inner) => match &**inner { + None => Some(it.next().ok_or(NErr::argument_error("index section: too few arguments".to_string()))?), + Some(e) => Some((*e).clone()), + } }; let hi = match hi { None => None, - Some(None) => Some(it.next().ok_or(NErr::argument_error("index section: too few arguments".to_string()))?), - Some(Some(e)) => Some((**e).clone()), + Some(inner) => match &**inner { + None => Some(it.next().ok_or(NErr::argument_error("index section: too few arguments".to_string()))?), + Some(e) => Some((*e).clone()), + } }; slice(x, lo, hi) } diff --git a/src/lib.rs b/src/lib.rs index d38f919..d18990b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1978,19 +1978,16 @@ fn obj_in(a: Obj, b: Obj) -> NRes { } } -pub fn warn(env: &Rc>, expr: &LocExpr) { +pub fn warn(env: &Rc>, expr: &LocExpr) -> LocExpr { let mut frenv = FreezeEnv { bound: HashSet::new(), env: Rc::clone(&env), warn: true, }; match freeze(&mut frenv, &expr) { - Ok(_) => {} + Ok(x) => x, Err(e) => { - eprintln!( - "\x1b[1;33mWARNING\x1b[0;33m: expr warn failed: {}\x1b[0m", - e - ); + panic!("\x1b[1;31mERROR\x1b[0;33m: expr warn failed: {}\x1b[0m", e); } } } diff --git a/src/main.rs b/src/main.rs index 502061c..8f38ea9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,7 +84,7 @@ fn run_code(code: &str, args: Vec, invoke_wrapper: Option<&'static str>, match parse(&code) { Ok(Some(expr)) => { - warn(&e, &expr); + let expr = warn(&e, &expr); let wrapped_expr = match invoke_wrapper { Some(wrap_id) => { let wrapper = Env::try_borrow_get_var(&e, wrap_id) @@ -132,6 +132,10 @@ fn get_wrapper(arg: &str) -> Option> { } fn main() { + // println!("{}", std::mem::size_of::()); + // println!("{}", std::mem::size_of::()); + // println!("{}", std::mem::size_of::()); + // println!("{}", std::mem::size_of::()); let mut args = std::env::args().collect::>(); if args.len() <= 1 { #[cfg(feature = "cli")] diff --git a/src/nnum.rs b/src/nnum.rs index 22dfdda..e75858d 100644 --- a/src/nnum.rs +++ b/src/nnum.rs @@ -17,10 +17,11 @@ use std::ops::{AddAssign, SubAssign}; use crate::gamma; +// wow, BigRationals are huge #[derive(Debug, Clone)] pub enum NNum { Int(BigInt), - Rational(BigRational), + Rational(Box), Float(f64), Complex(Complex64), } @@ -37,7 +38,7 @@ macro_rules! forward_display { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { NNum::Int(n) => fmt::$intimpl::fmt(n, formatter), - NNum::Rational(n) => fmt::$intimpl::fmt(n, formatter), + NNum::Rational(n) => fmt::$intimpl::fmt(&**n, formatter), NNum::Float(f) => fmt::$floatimpl::fmt(f, formatter), NNum::Complex(z) => fmt::$floatimpl::fmt(z, formatter), } @@ -60,7 +61,7 @@ impl From for NNum { } impl From for NNum { fn from(x: BigRational) -> Self { - NNum::Rational(x) + NNum::Rational(Box::new(x)) } } /* @@ -215,7 +216,7 @@ impl NNum { pub fn abs(&self) -> NNum { match self { NNum::Int(k) => NNum::Int(k.abs()), - NNum::Rational(r) => NNum::Rational(r.abs()), + NNum::Rational(r) => NNum::Rational(Box::new(r.abs())), NNum::Float(f) => NNum::Float(f.abs()), NNum::Complex(z) => NNum::Float(z.norm()), } @@ -260,7 +261,7 @@ impl NNum { pub fn to_rational(&self) -> Option { match self { NNum::Int(i) => Some(BigRational::from(i.clone())), - NNum::Rational(r) => Some(r.clone()), + NNum::Rational(r) => Some(*r.clone()), NNum::Float(_) => None, NNum::Complex(_) => None, } @@ -326,7 +327,7 @@ impl NNum { pub fn imaginary_part(&self) -> NNum { match self { NNum::Int(_) => NNum::Int(BigInt::from(0)), - NNum::Rational(_) => NNum::Rational(BigRational::from(BigInt::from(0))), + NNum::Rational(_) => NNum::Rational(Box::new(BigRational::from(BigInt::from(0)))), NNum::Float(_) => NNum::Float(0.0), NNum::Complex(z) => NNum::Float(z.im), } @@ -339,7 +340,7 @@ impl NNum { pub fn pow(&self, e: u32) -> NNum { match self { NNum::Int(i) => NNum::Int(i.pow(e)), - NNum::Rational(r) => NNum::Rational(r.pow(e as i32)), + NNum::Rational(r) => NNum::Rational(Box::new((&**r).pow(e as i32))), NNum::Float(f) => NNum::Float(f.powi(e as i32)), NNum::Complex(z) => NNum::Complex(z.powi(e as i32)), } @@ -353,7 +354,7 @@ impl NNum { } (NNum::Int(a), NNum::Float(b)) => powf_pdnum(bigint_to_f64_or_inf(a), *b), - (NNum::Rational(a), NNum::Int(b)) => NNum::from(Pow::pow(a, b)), + (NNum::Rational(a), NNum::Int(b)) => NNum::from(Pow::pow(&**a, b)), (NNum::Rational(a), NNum::Rational(b)) => { powf_pdnum(rational_to_f64_or_inf(a), rational_to_f64_or_inf(b)) } @@ -684,6 +685,18 @@ impl SoftDeref for &f64 { *self } } +impl SoftDeref for Box { + type Output = BigRational; + fn soft_deref(self) -> BigRational { + *self + } +} +impl<'a> SoftDeref for &'a Box { + type Output = &'a BigRational; + fn soft_deref(self) -> &'a BigRational { + &**self + } +} // ???????? macro_rules! binary_match { @@ -695,9 +708,9 @@ macro_rules! binary_match { (NNum::Float(fa), b) => NNum::Float($floatmethod(fa.soft_deref(), b.to_f64_or_inf_or_complex().expect("complex not elim"))), (a, NNum::Float(fb)) => NNum::Float($floatmethod(a.to_f64_or_inf_or_complex().expect("complex not elim"), fb.soft_deref())), - (NNum::Rational(ra), NNum::Rational(rb)) => NNum::Rational($ratmethod(ra, rb)), - (NNum::Rational(ra), NNum::Int(b)) => NNum::Rational($ratmethod(ra, &BigRational::from(b.clone()))), - (NNum::Int(a), NNum::Rational(rb)) => NNum::Rational($ratmethod(&BigRational::from(a.clone()), rb)), + (NNum::Rational(ra), NNum::Rational(rb)) => NNum::Rational(Box::new($ratmethod(ra.soft_deref(), rb.soft_deref()))), + (NNum::Rational(ra), NNum::Int(b)) => NNum::Rational(Box::new($ratmethod(ra.soft_deref(), &BigRational::from(b.clone())))), + (NNum::Int(a), NNum::Rational(rb)) => NNum::Rational(Box::new($ratmethod(&BigRational::from(a.clone()), rb.soft_deref()))), (NNum::Int (a), NNum::Int (b)) => NNum::Int($intmethod(a, b)), } @@ -825,7 +838,7 @@ impl Div<&NNum> for &NNum { type Output = NNum; fn div(self, other: &NNum) -> NNum { match (self.to_rational(), other.to_rational()) { - (Some(a), Some(b)) if !b.is_zero() => NNum::Rational(a / b), + (Some(a), Some(b)) if !b.is_zero() => NNum::Rational(Box::new(a / b)), _ => { let a = self.to_f64_or_inf_or_complex(); let b = other.to_f64_or_inf_or_complex(); @@ -848,7 +861,7 @@ impl Neg for NNum { fn neg(self) -> NNum { match self { NNum::Int(n) => NNum::Int(-n), - NNum::Rational(n) => NNum::Rational(-n), + NNum::Rational(n) => NNum::Rational(Box::new(-*n)), NNum::Float(f) => NNum::Float(-f), NNum::Complex(z) => NNum::Complex(-z), } @@ -860,7 +873,7 @@ impl Neg for &NNum { fn neg(self) -> NNum { match self { NNum::Int(n) => NNum::Int(-n), - NNum::Rational(n) => NNum::Rational(-n), + NNum::Rational(n) => NNum::Rational(Box::new(-&**n)), NNum::Float(f) => NNum::Float(-f), NNum::Complex(z) => NNum::Complex(-z), } From 2aef81afb5f2fed4238287dcb313f2d197cbc1c4 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Sat, 21 Oct 2023 10:46:14 -0700 Subject: [PATCH 04/23] lazy trace and var-lookup errors --- src/core.rs | 9 ++++-- src/eval.rs | 87 +++++++++++++++++++++++++++++------------------------ 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/src/core.rs b/src/core.rs index cfa5d3d..d62e939 100644 --- a/src/core.rs +++ b/src/core.rs @@ -4065,10 +4065,15 @@ pub fn default_precedence(name: &str) -> f64 { .unwrap_or(0.0) } -pub fn add_trace(res: NRes, thing: String, start: CodeLoc, end: CodeLoc) -> NRes { +pub fn add_trace( + res: NRes, + thing: impl FnOnce() -> String, + start: CodeLoc, + end: CodeLoc, +) -> NRes { match res { Err(NErr::Throw(e, mut trace)) => { - trace.push((thing, start, end, None)); + trace.push((thing(), start, end, None)); Err(NErr::Throw(e, trace)) } e => e, diff --git a/src/eval.rs b/src/eval.rs index 740a3cf..86a6253 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -112,7 +112,7 @@ impl ChainEvaluator { lhs.extend(rhs); self.operands.push(vec![add_trace( op.run(env, lhs), - format!("chain {}", op), + || format!("chain {}", op), start, end, )?]); @@ -506,7 +506,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } Expr::Ident(s) => add_trace( Env::try_borrow_get_var(env, s), - format!("ident"), + || format!("ident"), expr.start, expr.end, ), @@ -570,7 +570,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { let ir = eval_index_or_slice(env, i)?; add_trace( index_or_slice(xr, &ir), - format!("index/slice"), + || format!("index/slice"), expr.start, expr.end, ) @@ -629,7 +629,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { let oprd = evaluate(env, opd)?; add_trace( b.run(env, vec![lhs, oprd]), - format!("chain {}", oprr), + || format!("chain {}", oprr), oper.start, oper.end, ) @@ -693,7 +693,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::Assign(every, pat, rhs) => { let p = add_trace( eval_lvalue(env, pat), - format!("assign lvalue"), + || format!("assign lvalue"), expr.start, expr.end, )?; @@ -710,7 +710,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } else { assign(&env, &p, None, res) }; - add_trace(ret, format!("assign"), expr.start, expr.end)?; + add_trace(ret, || format!("assign"), expr.start, expr.end)?; Ok(Obj::Null) } Expr::Annotation(s, _) => evaluate(env, s), @@ -749,7 +749,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { expr.end, )), }, - format!("pop"), + || "pop".to_string(), expr.start, expr.end, ), @@ -804,7 +804,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { }, _ => Err(NErr::type_error("can't pop, weird pattern".to_string())), }, - format!("remove"), + || format!("remove"), expr.start, expr.end, ), @@ -831,7 +831,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { }?; add_trace( modify_every(env, &p, &mut |x| ff.run(env, vec![x, res.clone()])), - format!("op({})-assign", ff), + || format!("op({})-assign", ff), expr.start, expr.end, )?; @@ -860,14 +860,14 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { // consuming modifiers. Instead it's now enshrined into the semantics. add_trace( drop_lhs(&env, &p), - format!("null-assign"), + || format!("null-assign"), expr.start, expr.end, )?; let fres = ff.run(env, vec![pv, res])?; add_trace( assign(&env, &p, None, fres), - format!("op({})-assign", ff), + || format!("op({})-assign", ff), expr.start, expr.end, )?; @@ -894,8 +894,14 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { match (fr, acc) { // no section (Some(f), Ok(v)) => { - let fs = format!("call to {}", f); - add_trace(call_or_part_apply(env, f, v), fs, expr.start, expr.end) + // FIXME hmm, eager format not great... + let f_for_error = f.clone(); + add_trace( + call_or_part_apply(env, f, v), + || format!("call to {}", f_for_error), + expr.start, + expr.end, + ) } // some section @@ -967,14 +973,14 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { for (i, x) in xs[..xs.len() - 1].iter().enumerate() { add_trace( evaluate(env, x), - format!(";-sequence({}/{})", i + 1, xs.len()), + || format!(";-sequence({}/{})", i + 1, xs.len()), expr.start, expr.end, )?; } let ret = add_trace( evaluate(env, xs.last().unwrap()), - format!(";-sequence({}/{})", xs.len(), xs.len()), + || format!(";-sequence({}/{})", xs.len(), xs.len()), expr.start, expr.end, )?; @@ -987,14 +993,14 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::If(cond, if_body, else_body) => { let cr = add_trace( evaluate(env, cond), - "if-cond".to_string(), + || "if-cond".to_string(), expr.start, expr.end, )?; if cr.truthy() { add_trace( evaluate(env, if_body), - "if-branch".to_string(), + || "if-branch".to_string(), expr.start, expr.end, ) @@ -1002,7 +1008,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { match else_body { Some(b) => add_trace( evaluate(env, b), - "else-branch".to_string(), + || "else-branch".to_string(), expr.start, expr.end, ), @@ -1079,7 +1085,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } } }, - "for loop".to_string(), + || "for loop".to_string(), expr.start, expr.end, ) @@ -1090,7 +1096,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { let ee = Env::with_parent(env); if !(add_trace( evaluate(&ee, cond), - "while-cond".to_string(), + || "while-cond".to_string(), expr.start, expr.end, )? @@ -1100,7 +1106,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } match add_trace( evaluate(&ee, body), - "while-body".to_string(), + || "while-body".to_string(), expr.start, expr.end, ) { @@ -1114,7 +1120,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::Switch(scrutinee, arms) => { let s = add_trace( evaluate(env, scrutinee), - "switchee".to_string(), + || "switchee".to_string(), expr.start, expr.end, )?; @@ -1131,7 +1137,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { std::mem::drop(s); return add_trace( evaluate(&ee, body), - "switch-case".to_string(), + || "switch-case".to_string(), expr.start, expr.end, ); @@ -1144,7 +1150,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { "no case matched switch scrutinee: {}", s ))), - "switch".to_string(), + || "switch".to_string(), expr.start, expr.end, ) @@ -1245,7 +1251,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { }, a => Err(NErr::type_error(format!("can't import: {}", a))), }, - "import".to_string(), + || "import".to_string(), expr.start, expr.end, ) @@ -1281,7 +1287,7 @@ impl Closure { || Ok(args), "Wrong number of arguments", ), - "argument receiving".to_string(), + || "argument receiving".to_string(), self.body.start, self.body.end, )?; @@ -1289,7 +1295,7 @@ impl Closure { Err(NErr::Return(k)) => Ok(k), x => add_trace( x, - "closure call".to_string(), + || "closure call".to_string(), self.body.start, self.body.end, ), @@ -1997,7 +2003,7 @@ pub fn assign_respecting_type( } else { Ok(()) } - }).ok_or(NErr::name_error(if ixs.is_empty() { + }).ok_or_else(|| NErr::name_error(if ixs.is_empty() { format!("Variable {:?} not found (use := to declare)", s) } else { format!("Variable {:?} not found, couldn't set into index {:?}", s, ixs) @@ -2151,14 +2157,16 @@ pub fn drop_lhs(env: &REnv, lhs: &EvaluatedLvalue) -> NRes<()> { )?; Ok(()) }) - .ok_or(NErr::name_error(if ixs.is_empty() { - format!("Variable {:?} not found (use := to declare)", s) - } else { - format!( - "Variable {:?} not found, couldn't set into index {:?}", - s, ixs - ) - }))?, + .ok_or_else(|| { + NErr::name_error(if ixs.is_empty() { + format!("Variable {:?} not found (use := to declare)", s) + } else { + format!( + "Variable {:?} not found, couldn't set into index {:?}", + s, ixs + ) + }) + })?, EvaluatedLvalue::CommaSeq(ss) => drop_lhs_all(env, ss), EvaluatedLvalue::Annotation(..) => Err(NErr::syntax_error( "can't drop LHS with annotations in it for op-assign??".to_string(), @@ -2285,10 +2293,9 @@ pub fn modify_every( ))) } }) - .ok_or(NErr::name_error(format!( - "Variable {:?} not found in modify every", - s - )))? + .ok_or_else(|| { + NErr::name_error(format!("Variable {:?} not found in modify every", s)) + })? } else { modify_every_existing_index(&mut old, ixs, &mut |x: &mut Obj| { *x = rhs(std::mem::take(x))?; From 0d7711e70e010c43b17cbe985d8240c946beeaff Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Sun, 22 Oct 2023 22:08:50 -0700 Subject: [PATCH 05/23] specialize nums to nums --- src/lib.rs | 124 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d18990b..0d19a94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ impl Builtin for Plus { "+ only accepts numbers (or vectors), got {}", a ))), - Few2::Two(a, b) => expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a + b)), a, b, "+"), + Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(|a, b| a + b, a, b, "+"), f => Err(NErr::argument_error(format!( "+ only accepts two numbers, got {}", f.len() @@ -130,7 +130,7 @@ impl Builtin for Minus { Few2::Zero => Err(NErr::argument_error("received 0 args".to_string())), Few2::One(s) => expect_nums_and_vectorize_1(|x| Ok(Obj::Num(-x)), s, "unary -"), Few2::Two(a, b) => { - expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a - b)), a, b, "binary -") + expect_nums_and_vectorize_2_nums(|a, b| a - b, a, b, "binary -") } Few2::Many(a) => err_add_name(Err(NErr::argument_error_args(&a)), "-"), } @@ -171,7 +171,7 @@ impl Builtin for Times { "* only accepts numbers (or vectors), got {}", a ))), - Few2::Two(a, b) => expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a * b)), a, b, "*"), + Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(|a, b| a * b, a, b, "*"), f => Err(NErr::argument_error(format!( "* only accepts two numbers, got {}", f.len() @@ -223,7 +223,7 @@ impl Builtin for Divide { "/ only accepts numbers (or vectors), got {}", a ))), - Few2::Two(a, b) => expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a / b)), a, b, "/"), + Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(|a, b| a / b, a, b, "/"), f => Err(NErr::argument_error(format!( "/ only accepts two numbers, got {}", f.len() @@ -1760,6 +1760,76 @@ impl Builtin for NumsBuiltin { } } +// converting in and out of nums is a bit of a hot path sometimes?? +#[derive(Debug, Clone)] +pub struct TwoNumsToNumsBuiltin { + name: String, + body: fn(a: NNum, b: NNum) -> NNum, +} + +fn expect_nums_and_vectorize_2_nums( + body: fn(NNum, NNum) -> NNum, + a: Obj, + b: Obj, + name: &str, +) -> NRes { + match (a, b) { + (Obj::Num(a), Obj::Num(b)) => Ok(Obj::Num(body(a, b))), + (Obj::Num(a), Obj::Seq(Seq::Vector(mut b))) => Ok(Obj::Seq(Seq::Vector(Rc::new( + RcVecIter::of(&mut b).map(|e| body(a.clone(), e)).collect() + )))), + (Obj::Seq(Seq::Vector(mut a)), Obj::Num(b)) => Ok(Obj::Seq(Seq::Vector(Rc::new( + RcVecIter::of(&mut a).map(|e| body(e, b.clone())).collect() + )))), + (Obj::Seq(Seq::Vector(mut a)), Obj::Seq(Seq::Vector(mut b))) => { + if a.len() == b.len() { + Ok(Obj::Seq(Seq::Vector(Rc::new( + RcVecIter::of(&mut a) + .zip(RcVecIter::of(&mut b)) + .map(|(a, b)| body(a, b)) + .collect() + )))) + } else { + Err(NErr::value_error(format!( + "{}: couldn't vectorize, different lengths: {}, {}", + name, + a.len(), + b.len() + ))) + } + } + (a, b) => Err(NErr::argument_error(format!( + "{} only accepts numbers (or vectors), got {}, {}", + name, a, b + ))), + } +} + +impl Builtin for TwoNumsToNumsBuiltin { + fn run(&self, _env: &REnv, args: Vec) -> NRes { + match few2(args) { + // partial application, spicy + Few2::One(arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_)))) => { + Ok(clone_and_part_app_2(self, arg)) + } + Few2::One(a) => Err(NErr::argument_error(format!( + "{} only accepts numbers (or vectors), got {}", + self.name, a + ))), + Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(self.body, a, b, &self.name), + f => Err(NErr::argument_error(format!( + "{} only accepts two numbers, got {}", + self.name, + f.len() + ))), + } + } + + fn builtin_name(&self) -> &str { + &self.name + } +} + #[derive(Debug, Clone)] pub struct TwoNumsBuiltin { name: String, @@ -2708,34 +2778,34 @@ pub fn initialize(env: &mut Env) { Few2::Zero => Err(NErr::argument_error("received 0 args".to_string())), Few2::One(s) => expect_nums_and_vectorize_1(|x| Ok(Obj::Num(!x)), s, "unary ~"), Few2::Two(a, b) => { - expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a ^ b)), a, b, "binary ~") + expect_nums_and_vectorize_2_nums(|a, b| a ^ b, a, b, "binary ~") } Few2::Many(_) => Err(NErr::argument_error("received >2 args".to_string())), }, }); // for partial application - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "subtract".to_string(), - body: |a, b| Ok(Obj::Num(a - b)), + body: |a, b| a - b, }); - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "xor".to_string(), - body: |a, b| Ok(Obj::Num(a ^ b)), + body: |a, b| a ^ b, }); - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "⊕".to_string(), - body: |a, b| Ok(Obj::Num(a ^ b)), + body: |a, b| a ^ b, }); env.insert_builtin(Times); env.insert_builtin(SeqAndMappedFoldBuiltin { name: "sum".to_string(), identity: Obj::zero(), - body: |s, f| expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a + b)), s, f, "inner +"), + body: |s, f| expect_nums_and_vectorize_2_nums(|a, b| a + b, s, f, "inner +"), }); env.insert_builtin(SeqAndMappedFoldBuiltin { name: "product".to_string(), identity: Obj::one(), - body: |s, f| expect_nums_and_vectorize_2(|a, b| Ok(Obj::Num(a * b)), s, f, "inner *"), + body: |s, f| expect_nums_and_vectorize_2_nums(|a, b| a * b, s, f, "inner *"), }); env.insert_builtin(ComparisonOperator::of("==", |a, b| Ok(a == b))); env.insert_builtin(ComparisonOperator::of("!=", |a, b| Ok(a != b))); @@ -2758,9 +2828,9 @@ pub fn initialize(env: &mut Env) { "≥", ); env.insert_builtin(Divide); - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "%".to_string(), - body: |a, b| Ok(Obj::Num(a % b)), + body: |a, b| a % b, }); env.insert_builtin(TwoNumsBuiltin { name: "//".to_string(), @@ -2800,21 +2870,21 @@ pub fn initialize(env: &mut Env) { } }, }); - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "gcd".to_string(), - body: |a, b| Ok(Obj::Num(a.gcd(&b))), + body: |a, b| a.gcd(&b), }); - env.insert_rassoc_builtin(TwoNumsBuiltin { + env.insert_rassoc_builtin(TwoNumsToNumsBuiltin { name: "^".to_string(), - body: |a, b| Ok(Obj::Num(a.pow_num(&b))), + body: |a, b| a.pow_num(&b), }); - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "&".to_string(), - body: |a, b| Ok(Obj::Num(a & b)), + body: |a, b| a & b, }); - env.insert_builtin(TwoNumsBuiltin { + env.insert_builtin(TwoNumsToNumsBuiltin { name: "|".to_string(), - body: |a, b| Ok(Obj::Num(a | b)), + body: |a, b| a | b, }); /* env.insert_builtin(TwoNumsBuiltin { @@ -2823,16 +2893,16 @@ pub fn initialize(env: &mut Env) { }); */ env.insert_builtin_with_precedence( - TwoNumsBuiltin { + TwoNumsToNumsBuiltin { name: "<<".to_string(), - body: |a, b| Ok(Obj::Num(a << b)), + body: |a, b| a << b, }, Precedence(EXPONENT_PRECEDENCE, Assoc::Left), ); env.insert_builtin_with_precedence( - TwoNumsBuiltin { + TwoNumsToNumsBuiltin { name: ">>".to_string(), - body: |a, b| Ok(Obj::Num(a >> b)), + body: |a, b| a >> b, }, Precedence(EXPONENT_PRECEDENCE, Assoc::Left), ); From 893a0c17b26b73e6a2ce53365627e8ccc488993a Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 00:28:24 -0700 Subject: [PATCH 06/23] optimization: skip few2 in one example --- src/core.rs | 8 ++++++++ src/eval.rs | 14 +++++++++++++- src/lib.rs | 24 +++++++++++++++--------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/core.rs b/src/core.rs index d62e939..2b08973 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1936,6 +1936,14 @@ fn to_archetypes(lvalue: &Lvalue) -> Vec { pub trait Builtin: Debug { fn run(&self, env: &REnv, args: Vec) -> NRes; + // hot paths + fn run1(&self, env: &REnv, arg: Obj) -> NRes { + self.run(env, vec![arg]) + } + fn run2(&self, env: &REnv, arg1: Obj, arg2: Obj) -> NRes { + self.run(env, vec![arg1, arg2]) + } + // Used for builtins to identify each other, since comparing pointers is bad (?) // https://rust-lang.github.io/rust-clippy/master/#vtable_address_comparisons fn builtin_name(&self) -> &str; diff --git a/src/eval.rs b/src/eval.rs index 86a6253..41b5878 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -628,7 +628,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { if let Obj::Func(b, _prec) = &oprr { let oprd = evaluate(env, opd)?; add_trace( - b.run(env, vec![lhs, oprd]), + b.run2(env, lhs, oprd), || format!("chain {}", oprr), oper.start, oper.end, @@ -2501,6 +2501,18 @@ impl Func { } } } + pub fn run1(&self, env: &REnv, arg: Obj) -> NRes { + match self { + Func::Builtin(b) => b.run1(env, arg), + _ => self.run(env, vec![arg]), + } + } + pub fn run2(&self, env: &REnv, arg1: Obj, arg2: Obj) -> NRes { + match self { + Func::Builtin(b) => b.run2(env, arg1, arg2), + _ => self.run(env, vec![arg1, arg2]), + } + } fn try_chain(&self, other: &Func) -> Option { match self { diff --git a/src/lib.rs b/src/lib.rs index 0d19a94..6d0ad53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,21 +75,27 @@ impl Builtin for Plus { // copypasta fn run(&self, _env: &REnv, args: Vec) -> NRes { match few2(args) { - // partial application, spicy - Few2::One(arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_)))) => { - Ok(clone_and_part_app_2(self, arg)) - } - Few2::One(a) => Err(NErr::argument_error(format!( - "+ only accepts numbers (or vectors), got {}", - a - ))), - Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(|a, b| a + b, a, b, "+"), + Few2::One(a) => self.run1(_env, a), + Few2::Two(a, b) => self.run2(_env, a, b), f => Err(NErr::argument_error(format!( "+ only accepts two numbers, got {}", f.len() ))), } } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + match arg { + // partial application, spicy + Obj::Num(_) | Obj::Seq(Seq::Vector(_)) => Ok(clone_and_part_app_2(self, arg)), + a => Err(NErr::argument_error(format!( + "+ only accepts numbers (or vectors), got {}", + a + ))), + } + } + fn run2(&self, _env: &REnv, arg1: Obj, arg2: Obj) -> NRes { + expect_nums_and_vectorize_2_nums(|a, b| a + b, arg1, arg2, "+") + } fn builtin_name(&self) -> &str { "+" From 94bf744b42f4f2a32ac0ffc1dbc40f1dc42c9bcd Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 18:26:32 -0700 Subject: [PATCH 07/23] specialize more run1, run2 --- src/core.rs | 51 +++++---- src/eval.rs | 32 ++++-- src/lib.rs | 280 +++++++++++++++++++++++++++---------------------- src/streams.rs | 6 +- 4 files changed, 212 insertions(+), 157 deletions(-) diff --git a/src/core.rs b/src/core.rs index 2b08973..56a50d7 100644 --- a/src/core.rs +++ b/src/core.rs @@ -551,13 +551,13 @@ pub fn type_of(obj: &Obj) -> ObjType { pub fn expect_one(args: Vec, msg: &str) -> NRes { match few(args) { Few::One(arg) => Ok(arg), - _ => Err(NErr::type_error(format!("{} expected one argument", msg))), + t => Err(NErr::type_error(format!("{} expected one argument, got {}", msg, t.len()))), } } -pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { +pub fn call_type1(ty: &ObjType, arg: Obj) -> NRes { match ty { - ObjType::Int => match expect_one(arg, "int")? { + ObjType::Int => match arg { Obj::Num(n) => Ok(Obj::Num( n.trunc() .ok_or(NErr::value_error("can't coerce to int".to_string()))?, @@ -570,7 +570,7 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { "int: expected number or string".to_string(), )), }, - ObjType::Float => match expect_one(arg, "float")? { + ObjType::Float => match arg { Obj::Num(n) => Ok(Obj::from(to_f64_ok(&n)?)), Obj::Seq(Seq::String(s)) => match s.parse::() { Ok(x) => Ok(Obj::from(x)), @@ -580,7 +580,7 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { "float: expected number or string".to_string(), )), }, - ObjType::Number => match expect_one(arg, "number")? { + ObjType::Number => match arg { Obj::Num(n) => Ok(Obj::Num(n)), Obj::Seq(Seq::String(s)) => match s.parse::() { Ok(x) => Ok(Obj::from(x)), @@ -593,14 +593,14 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { "number: expected number or string".to_string(), )), }, - ObjType::List => match expect_one(arg, "list")? { + ObjType::List => match arg { Obj::Seq(Seq::List(xs)) => Ok(Obj::Seq(Seq::List(xs))), mut arg => Ok(Obj::list( mut_obj_into_iter(&mut arg, "list conversion")?.collect::>>()?, )), }, - ObjType::String => Ok(Obj::from(format!("{}", expect_one(arg, "str")?))), - ObjType::Bytes => match expect_one(arg, "bytes")? { + ObjType::String => Ok(Obj::from(format!("{}", arg))), + ObjType::Bytes => match arg { Obj::Seq(Seq::Bytes(xs)) => Ok(Obj::Seq(Seq::Bytes(xs))), Obj::Seq(Seq::String(s)) => Ok(Obj::Seq(Seq::Bytes(Rc::new(s.as_bytes().to_vec())))), mut arg => Ok(Obj::Seq(Seq::Bytes(Rc::new( @@ -609,11 +609,11 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { .collect::>>()?, )))), }, - ObjType::Vector => match expect_one(arg, "vector")? { + ObjType::Vector => match arg { Obj::Seq(Seq::Vector(s)) => Ok(Obj::Seq(Seq::Vector(s))), mut arg => to_obj_vector(mut_obj_into_iter(&mut arg, "vector conversion")?), }, - ObjType::Dict => match expect_one(arg, "dict")? { + ObjType::Dict => match arg { Obj::Seq(Seq::Dict(x, d)) => Ok(Obj::Seq(Seq::Dict(x, d))), // nonsensical but maybe this is something some day /* @@ -634,26 +634,35 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { )), }, ObjType::Type => Ok(Obj::Func( - Func::Type(type_of(&expect_one(arg, "type")?)), + Func::Type(type_of(&arg)), Precedence::zero(), )), + ObjType::Struct(_) => { + call_type(ty, vec![arg]) + } + // TODO: complex + _ => Err(NErr::type_error( + "that type can't be called (maybe not implemented)".to_string(), + )), + } +} + +pub fn call_type(ty: &ObjType, args: Vec) -> NRes { + match ty { ObjType::Struct(s) => { - if arg.len() == 0 { + if args.len() == 0 { Ok(Obj::Instance(s.clone(), vec![Obj::Null; s.size])) - } else if arg.len() == s.size { - Ok(Obj::Instance(s.clone(), arg)) + } else if args.len() == s.size { + Ok(Obj::Instance(s.clone(), args)) } else { Err(NErr::argument_error(format!( "struct construction: wrong number of arguments: {}, wanted {}", - arg.len(), + args.len(), s.size ))) } } - // TODO: complex - _ => Err(NErr::type_error( - "that type can't be called (maybe not implemented)".to_string(), - )), + _ => call_type1(ty, expect_one(args, &ty.name())?), } } @@ -1936,7 +1945,9 @@ fn to_archetypes(lvalue: &Lvalue) -> Vec { pub trait Builtin: Debug { fn run(&self, env: &REnv, args: Vec) -> NRes; - // hot paths + // there are a LOT of builtins who specialize based on how many arguments they get, and a LOT + // of paths where we know how many arguments we're passing, so allow the two to be "manually + // fused" fn run1(&self, env: &REnv, arg: Obj) -> NRes { self.run(env, vec![arg]) } diff --git a/src/eval.rs b/src/eval.rs index 41b5878..7848bf8 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -830,7 +830,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { _ => evaluate(env, rhs), }?; add_trace( - modify_every(env, &p, &mut |x| ff.run(env, vec![x, res.clone()])), + modify_every(env, &p, &mut |x| ff.run2(env, x, res.clone())), || format!("op({})-assign", ff), expr.start, expr.end, @@ -864,7 +864,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { expr.start, expr.end, )?; - let fres = ff.run(env, vec![pv, res])?; + let fres = ff.run2(env, pv, res)?; add_trace( assign(&env, &p, None, fres), || format!("op({})-assign", ff), @@ -1501,6 +1501,16 @@ pub fn eval_lvalue_as_obj(env: &REnv, expr: &EvaluatedLvalue) -> NRes { } } +pub fn call1(env: &REnv, f: Obj, arg: Obj) -> NRes { + match f { + Obj::Func(ff, _) => ff.run1(env, arg), + _ => Err(NErr::type_error(format!( + "Can't call non-function {}", + FmtObj::debug(&f) + ))), + } +} + pub fn call(env: &REnv, f: Obj, args: Vec) -> NRes { match f { Obj::Func(ff, _) => ff.run(env, args), @@ -2340,22 +2350,22 @@ impl Func { Func::Builtin(b) => b.run(env, args), Func::Closure(c) => c.run(args), Func::PartialApp1(f, x) => match few(args) { - Few::One(arg) => f.run(env, vec![(**x).clone(), arg]), + Few::One(arg) => f.run2(env, (**x).clone(), arg), a => Err(NErr::argument_error(format!("partially applied functions can only be called with one more argument, but {} {} got {}", f, FmtObj::debug(x), a))) } Func::PartialApp2(f, x) => match few(args) { - Few::One(arg) => f.run(env, vec![arg, (**x).clone()]), + Few::One(arg) => f.run2(env, arg, (**x).clone()), a => Err(NErr::argument_error(format!("partially applied functions can only be called with one more argument, but {} {} got {}", f, FmtObj::debug(x), a))) } Func::PartialAppLast(f, x) => { args.push(*x.clone()); f.run(env, args) } - Func::Composition(f, g) => f.run(env, vec![g.run(env, args)?]), + Func::Composition(f, g) => f.run1(env, g.run(env, args)?), Func::OnComposition(f, g) => { let mut mapped_args = Vec::new(); for e in args { - mapped_args.push(g.run(env, vec![e])?); + mapped_args.push(g.run1(env, e)?); } f.run(env, mapped_args) } @@ -2366,7 +2376,7 @@ impl Func { match seq.len() { Some(n) if n == fs.len() => { for (f, a) in fs.iter().zip(mut_seq_into_iter(&mut seq)) { - res.push(f.run(env, vec![a?])?); + res.push(f.run1(env, a?)?); } } Some(n) => return Err(NErr::type_error(format!("Parallel argument seq has wrong length {}: {:?}", n, seq))), @@ -2381,7 +2391,7 @@ impl Func { } Few::Many(args) => { for (f, a) in fs.iter().zip(args) { - res.push(f.run(env, vec![a])?); + res.push(f.run1(env, a)?); } } } @@ -2397,7 +2407,7 @@ impl Func { Func::Flip(f) => match few2(args) { // weird lol Few2::One(a) => Ok(Obj::Func(Func::PartialApp1(f.clone(), Box::new(a)), Precedence::zero())), - Few2::Two(a, b) => f.run(env, vec![b, a]), + Few2::Two(a, b) => f.run2(env, b, a), _ => Err(NErr::argument_error("Flipped function can only be called on two arguments".to_string())) } Func::ListSection(x) => { @@ -2504,6 +2514,8 @@ impl Func { pub fn run1(&self, env: &REnv, arg: Obj) -> NRes { match self { Func::Builtin(b) => b.run1(env, arg), + Func::PartialApp1(f, x) => f.run2(env, (**x).clone(), arg), + Func::PartialApp2(f, x) => f.run2(env, arg, (**x).clone()), _ => self.run(env, vec![arg]), } } @@ -2555,7 +2567,7 @@ pub fn is_type(ty: &ObjType, arg: &Obj) -> bool { (ObjType::Type, Obj::Func(Func::Type(_), _)) => true, (ObjType::Any, _) => true, (ObjType::Struct(s1), Obj::Instance(s2, _)) => s1.id == s2.id, - (ObjType::Satisfying(renv, func), x) => match func.run(renv, vec![x.clone()]) { + (ObjType::Satisfying(renv, func), x) => match func.run1(renv, x.clone()) { Ok(res) => res.truthy(), Err(e) => { eprintln!("INTERNAL ERROR: running the predicate for a 'satisfying'-type-check failed with; {}! trudging on...", e); diff --git a/src/lib.rs b/src/lib.rs index 6d0ad53..0a7d074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,6 +141,12 @@ impl Builtin for Minus { Few2::Many(a) => err_add_name(Err(NErr::argument_error_args(&a)), "-"), } } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + expect_nums_and_vectorize_1(|x| Ok(Obj::Num(-x)), arg, "unary -") + } + fn run2(&self, _env: &REnv, arg1: Obj, arg2: Obj) -> NRes { + expect_nums_and_vectorize_2_nums(|a, b| a - b, arg1, arg2, "binary -") + } fn builtin_name(&self) -> &str { "-" @@ -184,6 +190,10 @@ impl Builtin for Times { ))), } } + // FIXME + fn run2(&self, _env: &REnv, arg1: Obj, arg2: Obj) -> NRes { + expect_nums_and_vectorize_2_nums(|a, b| a * b, arg1, arg2, "*") + } fn builtin_name(&self) -> &str { "*" @@ -344,7 +354,7 @@ impl Builtin for ComparisonOperator { } for i in 0..self.chained.len() { let res = - self.chained[i].run(env, vec![args[i + 1].clone(), args[i + 2].clone()])?; + self.chained[i].run2(env, args[i + 1].clone(), args[i + 2].clone())?; if !res.truthy() { return Ok(res); } @@ -535,7 +545,7 @@ impl Builtin for Extremum { if match &ret { None => true, Some(r) => { - ncmp(&f.run(env, vec![b.clone(), r.clone()])?, &Obj::zero())? + ncmp(&f.run2(env, b.clone(), r.clone())?, &Obj::zero())? == self.bias } } { @@ -552,7 +562,7 @@ impl Builtin for Extremum { if match &ret { None => true, Some(r) => { - ncmp(&f.run(env, vec![b.clone(), r.clone()])?, &Obj::zero())? + ncmp(&f.run2(env, b.clone(), r.clone())?, &Obj::zero())? == self.bias } } { @@ -619,7 +629,7 @@ impl Builtin for Count { match b { Obj::Func(b, _) => { for e in it { - if b.run(env, vec![e?])?.truthy() { + if b.run1(env, e?)?.truthy() { c += 1; } } @@ -794,7 +804,27 @@ impl Builtin for ToBuiltin { fn run(&self, _env: &REnv, args: Vec) -> NRes { match few3(args) { Few3::One(a) => Ok(clone_and_part_app_2(self, a)), - Few3::Two(Obj::Num(a), Obj::Num(b)) => { + Few3::Two(a, b) => self.run2(_env, a, b), + Few3::Three(Obj::Num(a), Obj::Num(b), Obj::Num(c)) => { + let n1 = into_bigint_ok(a)?; + let n2 = into_bigint_ok(b)?; + let n3 = into_bigint_ok(c)?; + Ok(Obj::Seq(Seq::Stream(Rc::new(Range( + n1, + Some(if n3.is_negative() { + n2 - 1usize + } else { + n2 + 1usize + }), + n3, + ))))) + } + c => err_add_name(Err(NErr::argument_error_few3(&c)), "to"), + } + } + fn run2(&self, _env: &REnv, a: Obj, b: Obj) -> NRes { + match (a, b) { + (Obj::Num(a), Obj::Num(b)) => { let n1 = into_bigint_ok(a)?; let n2 = into_bigint_ok(b)?; Ok(Obj::Seq(Seq::Stream(Rc::new(Range( @@ -803,7 +833,7 @@ impl Builtin for ToBuiltin { BigInt::from(1), ))))) } - Few3::Two(Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { + (Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { let mut ac = a.chars(); let mut bc = b.chars(); match (ac.next(), ac.next(), bc.next(), bc.next()) { @@ -822,22 +852,8 @@ impl Builtin for ToBuiltin { ))), } } - Few3::Two(a, Obj::Func(Func::Type(t), _)) => call_type(&t, vec![a]), // sugar lmao - Few3::Three(Obj::Num(a), Obj::Num(b), Obj::Num(c)) => { - let n1 = into_bigint_ok(a)?; - let n2 = into_bigint_ok(b)?; - let n3 = into_bigint_ok(c)?; - Ok(Obj::Seq(Seq::Stream(Rc::new(Range( - n1, - Some(if n3.is_negative() { - n2 - 1usize - } else { - n2 + 1usize - }), - n3, - ))))) - } - c => err_add_name(Err(NErr::argument_error_few3(&c)), "to"), + (a, Obj::Func(Func::Type(t), _)) => call_type1(&t, a), // sugar lmao + (a, b) => err_add_name(Err(NErr::argument_error_2(&a, &b)), "to"), } } @@ -972,7 +988,7 @@ impl Builtin for ZipLongest { match &func { Some(f) => { for y in batch { - x = f.run(env, vec![x, y?])?; + x = f.run2(env, x, y?)?; } ret.push(x) } @@ -1114,7 +1130,7 @@ impl Builtin for Fold { let mut cur = cur0?; // not sure if any standard fallible rust methods work... for e in it { - cur = f.run(env, vec![cur, e?])?; + cur = f.run2(env, cur, e?)?; } Ok(cur) } @@ -1129,7 +1145,7 @@ impl Builtin for Fold { Obj::Func(f, _) => { // not sure if any standard fallible rust methods work... for e in it { - cur = f.run(env, vec![cur, e?])?; + cur = f.run2(env, cur, e?)?; } Ok(cur) } @@ -1172,7 +1188,7 @@ impl Builtin for Scan { let mut cur = cur0?; let mut acc = vec![cur.clone()]; for e in it { - cur = f.run(env, vec![cur, e?])?; + cur = f.run2(env, cur, e?)?; acc.push(cur.clone()); } Ok(Obj::list(acc)) @@ -1188,7 +1204,7 @@ impl Builtin for Scan { Obj::Func(f, _) => { let mut acc = vec![cur.clone()]; for e in it { - cur = f.run(env, vec![cur, e?])?; + cur = f.run2(env, cur, e?)?; acc.push(cur.clone()); } Ok(Obj::list(acc)) @@ -1261,7 +1277,7 @@ impl Builtin for Merge { None => *e.get_mut() = v, Some(f) => { let slot = e.get_mut(); - *slot = f.run(env, vec![std::mem::take(slot), v])?; + *slot = f.run2(env, std::mem::take(slot), v)?; } }, } @@ -1343,7 +1359,7 @@ impl Builtin for Replace { if status.is_err() { return String::new(); } - match f.run(env, vec![captures_to_obj(&r, caps)]) { + match f.run1(env, captures_to_obj(&r, caps)) { Ok(s) => format!("{}", s), Err(e) => { status = Err(e); @@ -1540,17 +1556,11 @@ pub struct OneArgBuiltin { impl Builtin for OneArgBuiltin { fn run(&self, _env: &REnv, args: Vec) -> NRes { - match few(args) { - Few::One(arg) => { - let ty = type_of(&arg); - err_add_name((self.body)(arg), &format!("{}({})", self.name, ty.name())) - } - f => Err(NErr::argument_error(format!( - "{} only accepts one argument, got {}", - self.name, - f.len() - ))), - } + self.run1(_env, expect_one(args, &self.name)?) + } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + let ty = type_of(&arg); + err_add_name((self.body)(arg), &format!("{}({})", self.name, ty.name())) } fn builtin_name(&self) -> &str { @@ -1569,7 +1579,7 @@ impl Builtin for TwoArgBuiltin { match few2(args) { // partial application, spicy Few2::One(arg) => Ok(clone_and_part_app_2(self, arg)), - Few2::Two(a, b) => err_add_name((self.body)(a, b), &self.name), + Few2::Two(a, b) => self.run2(_env, a, b), f => Err(NErr::argument_error(format!( "{} only accepts two arguments (or one for partial application), got {}", self.name, @@ -1577,24 +1587,12 @@ impl Builtin for TwoArgBuiltin { ))), } } - - fn builtin_name(&self) -> &str { - &self.name + fn run2(&self, _env: &REnv, a: Obj, b: Obj) -> NRes { + err_add_name((self.body)(a, b), &self.name) } -} - -#[derive(Debug, Clone)] -pub struct IdBuiltin; -impl Builtin for IdBuiltin { - fn run(&self, _env: &REnv, args: Vec) -> NRes { - match few(args) { - Few::One(arg) => Ok(arg), - a => err_add_name(Err(NErr::argument_error_few(&a)), "id"), - } - } fn builtin_name(&self) -> &str { - "id" + &self.name } } @@ -1627,8 +1625,24 @@ standard_three_part_debug!(SeqAndMappedFoldBuiltin); impl Builtin for SeqAndMappedFoldBuiltin { fn run(&self, env: &REnv, args: Vec) -> NRes { match few2(args) { - // partial application, spicy - Few2::One(Obj::Seq(mut s)) => { + Few2::One(a) => self.run1(env, a), + Few2::Two(Obj::Seq(mut s), Obj::Func(f, _)) => { + let mut state = self.identity.clone(); + for e in mut_seq_into_iter(&mut s) { + state = match (self.body)(state, f.run1(env, e?)?) { + Ok(r) => r, + Err(NErr::Break(r)) => return Ok(r.unwrap_or(Obj::Null)), + e @ Err(_) => return err_add_name(e, &self.name), + } + } + Ok(state) + } + a => err_add_name(Err(NErr::argument_error_few2(&a)), &self.name), + } + } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + match arg { + Obj::Seq(mut s) => { let mut state = self.identity.clone(); for e in mut_seq_into_iter(&mut s) { state = match (self.body)(state, e?) { @@ -1639,11 +1653,16 @@ impl Builtin for SeqAndMappedFoldBuiltin { } Ok(state) } - Few2::One(f @ Obj::Func(..)) => Ok(clone_and_part_app_2(self, f)), - Few2::Two(Obj::Seq(mut s), Obj::Func(f, _)) => { + f @ Obj::Func(..) => Ok(clone_and_part_app_2(self, f)), + a => err_add_name(Err(NErr::argument_error_1(&a)), &self.name), + } + } + fn run2(&self, env: &REnv, a: Obj, b: Obj) -> NRes { + match (a, b) { + (Obj::Seq(mut s), Obj::Func(f, _)) => { let mut state = self.identity.clone(); for e in mut_seq_into_iter(&mut s) { - state = match (self.body)(state, f.run(env, vec![e?])?) { + state = match (self.body)(state, f.run1(env, e?)?) { Ok(r) => r, Err(NErr::Break(r)) => return Ok(r.unwrap_or(Obj::Null)), e @ Err(_) => return err_add_name(e, &self.name), @@ -1651,7 +1670,7 @@ impl Builtin for SeqAndMappedFoldBuiltin { } Ok(state) } - a => err_add_name(Err(NErr::argument_error_few2(&a)), &self.name), + (a, b) => err_add_name(Err(NErr::argument_error_2(&a, &b)), &self.name), } } @@ -1679,14 +1698,11 @@ standard_three_part_debug!(EnvOneArgBuiltin); impl Builtin for EnvOneArgBuiltin { fn run(&self, env: &REnv, args: Vec) -> NRes { - match few(args) { - Few::One(arg) => err_add_name((self.body)(env, arg), &self.name), - f => Err(NErr::argument_error(format!( - "{} only accepts one argument, got {}", - self.name, - f.len() - ))), - } + let arg = expect_one(args, &self.name)?; + self.run1(env, arg) + } + fn run1(&self, env: &REnv, arg: Obj) -> NRes { + err_add_name((self.body)(env, arg), &self.name) } fn builtin_name(&self) -> &str { @@ -1706,7 +1722,7 @@ impl Builtin for EnvTwoArgBuiltin { match few2(args) { // partial application, spicy Few2::One(arg) => Ok(clone_and_part_app_2(self, arg)), - Few2::Two(a, b) => err_add_name((self.body)(env, a, b), &self.name), + Few2::Two(a, b) => self.run2(env, a, b), f => Err(NErr::argument_error(format!( "{} only accepts two arguments (or one for partial application), got {}", self.name, @@ -1714,6 +1730,9 @@ impl Builtin for EnvTwoArgBuiltin { ))), } } + fn run2(&self, env: &REnv, arg1: Obj, arg2: Obj) -> NRes { + err_add_name((self.body)(env, arg1, arg2), &self.name) + } fn builtin_name(&self) -> &str { &self.name @@ -1722,14 +1741,10 @@ impl Builtin for EnvTwoArgBuiltin { impl Builtin for OneNumBuiltin { fn run(&self, _env: &REnv, args: Vec) -> NRes { - match few(args) { - Few::One(x) => expect_nums_and_vectorize_1(self.body, x, &self.name), - f => Err(NErr::argument_error(format!( - "{} only accepts one argument, got {}", - self.name, - f.len() - ))), - } + self.run1(_env, expect_one(args, &self.name)?) + } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + expect_nums_and_vectorize_1(self.body, arg, &self.name) } fn builtin_name(&self) -> &str { @@ -1814,15 +1829,8 @@ fn expect_nums_and_vectorize_2_nums( impl Builtin for TwoNumsToNumsBuiltin { fn run(&self, _env: &REnv, args: Vec) -> NRes { match few2(args) { - // partial application, spicy - Few2::One(arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_)))) => { - Ok(clone_and_part_app_2(self, arg)) - } - Few2::One(a) => Err(NErr::argument_error(format!( - "{} only accepts numbers (or vectors), got {}", - self.name, a - ))), - Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(self.body, a, b, &self.name), + Few2::One(a) => self.run1(_env, a), + Few2::Two(a, b) => self.run2(_env, a, b), f => Err(NErr::argument_error(format!( "{} only accepts two numbers, got {}", self.name, @@ -1830,6 +1838,20 @@ impl Builtin for TwoNumsToNumsBuiltin { ))), } } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + match arg { + arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_))) => { + Ok(clone_and_part_app_2(self, arg)) + } + a => Err(NErr::argument_error(format!( + "{} only accepts numbers (or vectors), got {}", + self.name, a + ))), + } + } + fn run2(&self, _env: &REnv, a: Obj, b: Obj) -> NRes { + expect_nums_and_vectorize_2_nums(self.body, a, b, &self.name) + } fn builtin_name(&self) -> &str { &self.name @@ -1882,15 +1904,8 @@ fn expect_nums_and_vectorize_2( impl Builtin for TwoNumsBuiltin { fn run(&self, _env: &REnv, args: Vec) -> NRes { match few2(args) { - // partial application, spicy - Few2::One(arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_)))) => { - Ok(clone_and_part_app_2(self, arg)) - } - Few2::One(a) => Err(NErr::argument_error(format!( - "{} only accepts numbers (or vectors), got {}", - self.name, a - ))), - Few2::Two(a, b) => expect_nums_and_vectorize_2(self.body, a, b, &self.name), + Few2::One(a) => self.run1(_env, a), + Few2::Two(a, b) => self.run2(_env, a, b), f => Err(NErr::argument_error(format!( "{} only accepts two numbers, got {}", self.name, @@ -1898,6 +1913,20 @@ impl Builtin for TwoNumsBuiltin { ))), } } + fn run1(&self, _env: &REnv, arg: Obj) -> NRes { + match arg { + arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_))) => { + Ok(clone_and_part_app_2(self, arg)) + } + a => Err(NErr::argument_error(format!( + "{} only accepts numbers (or vectors), got {}", + self.name, a + ))), + } + } + fn run2(&self, _env: &REnv, a: Obj, b: Obj) -> NRes { + expect_nums_and_vectorize_2(self.body, a, b, &self.name) + } fn builtin_name(&self) -> &str { &self.name @@ -2187,7 +2216,7 @@ fn multi_sort(v: Seq) -> NRes { fn filtered>(env: &REnv, f: Func, v: Vec, neg: bool) -> NRes> { let mut ret = Vec::new(); for x in v { - if f.run(env, vec![x.clone().into()])?.truthy() != neg { + if f.run1(env, x.clone().into())?.truthy() != neg { ret.push(x) } } @@ -2202,7 +2231,7 @@ fn sorted_by>(env: &REnv, f: Func, mut v: Vec) -> NRes match ncmp(&k, &Obj::zero()) { Ok(ord) => ord, Err(e) => { @@ -2332,7 +2361,7 @@ fn take_while_inner>( ) -> NRes> { let mut acc = Vec::new(); while let Some(x) = it.next() { - if f.run(env, vec![x.clone().into()])?.truthy() { + if f.run1(env, x.clone().into())?.truthy() { acc.push(x) } else { return Ok(acc); @@ -2370,7 +2399,7 @@ fn take_while(s: Seq, f: Func, env: &REnv) -> NRes { let mut s = s.clone_box(); while let Some(x) = s.next() { let x = x?; - if f.run(env, vec![x.clone()])?.truthy() { + if f.run1(env, x.clone())?.truthy() { acc.push(x) } else { return Ok(Obj::list(acc)); @@ -2388,7 +2417,7 @@ fn drop_while_inner>( ) -> NRes> { let mut it = it.peekable(); while let Some(x) = it.peek() { - if f.run(env, vec![x.clone().into()])?.truthy() { + if f.run1(env, x.clone().into())?.truthy() { it.next(); } else { return Ok(it); @@ -2423,7 +2452,7 @@ fn drop_while(s: Seq, f: Func, env: &REnv) -> NRes { let mut t = s.clone_box(); while let Some(x) = t.peek() { let x = x?; - if f.run(env, vec![x.clone()])?.truthy() { + if f.run1(env, x.clone())?.truthy() { t.next(); } } @@ -2585,11 +2614,11 @@ fn multi_group_by_eq(v: Seq) -> NRes> { fn multi_group_by(env: &REnv, f: Func, v: Seq) -> NRes> { multimulti!( v, - grouped_by(v, |a, b| Ok(f.run(env, vec![a, b])?.truthy())) + grouped_by(v, |a, b| Ok(f.run2(env, a, b)?.truthy())) ) } fn multi_group_all_with(env: &REnv, f: Func, v: Seq) -> NRes> { - multimulti!(v, grouped_all_with(v, |x| f.run(env, vec![x]))) + multimulti!(v, grouped_all_with(v, |x| f.run1(env, x))) } fn multi_window(v: Seq, n: usize) -> NRes> { multimulti!(v, windowed(v, n)) @@ -3125,7 +3154,7 @@ pub fn initialize(env: &mut Env) { }); env.insert_builtin(EnvTwoArgBuiltin { name: "then".to_string(), - body: |env, a, b| call(env, b, vec![a]), + body: |env, a, b| call1(env, b, a), }); env.insert_builtin(EnvTwoArgBuiltin { name: "apply".to_string(), @@ -3181,15 +3210,15 @@ pub fn initialize(env: &mut Env) { }); env.insert_builtin(EnvTwoArgBuiltin { name: ".".to_string(), - body: |env, a, b| call(env, b, vec![a]), + body: |env, a, b| call1(env, b, a), }); env.insert_builtin(EnvTwoArgBuiltin { name: ".>".to_string(), - body: |env, a, b| call(env, b, vec![a]), + body: |env, a, b| call1(env, b, a), }); env.insert_builtin(EnvTwoArgBuiltin { name: "<.".to_string(), - body: |env, a, b| call(env, a, vec![b]), + body: |env, a, b| call1(env, a, b), }); env.insert_builtin(TwoArgBuiltin { name: ">>>".to_string(), @@ -3303,7 +3332,7 @@ pub fn initialize(env: &mut Env) { match b { Obj::Func(b, _) => { for e in it { - b.run(env, vec![e?])?; + b.run1(env, e?)?; } Ok(Obj::Null) } @@ -3318,7 +3347,7 @@ pub fn initialize(env: &mut Env) { let it = mut_obj_into_iter(&mut a, "map")?; match b { Obj::Func(b, _) => Ok(Obj::list( - it.map(|e| b.run(env, vec![e?])) + it.map(|e| b.run1(env, e?)) .collect::>>()?, )), _ => Err(NErr::type_error("not callable".to_string())), @@ -3333,7 +3362,7 @@ pub fn initialize(env: &mut Env) { (Obj::Seq(Seq::Dict(mut d, def)), Obj::Func(b, _)) => Ok(Obj::dict( Rc::make_mut(&mut d) .drain() - .map(|(k, v)| Ok((to_key(b.run(env, vec![key_to_obj(k)])?)?, v))) + .map(|(k, v)| Ok((to_key(b.run1(env, key_to_obj(k))?)?, v))) .collect::>>()?, def.map(|x| *x), )), @@ -3346,10 +3375,10 @@ pub fn initialize(env: &mut Env) { (Obj::Seq(Seq::Dict(mut d, def)), Obj::Func(b, _)) => Ok(Obj::dict( Rc::make_mut(&mut d) .drain() - .map(|(k, v)| Ok((k, b.run(env, vec![v])?))) + .map(|(k, v)| Ok((k, b.run1(env, v)?))) .collect::>>()?, match def { - Some(def) => Some(b.run(env, vec![*def])?), + Some(def) => Some(b.run1(env, *def)?), None => None, }, )), @@ -3397,7 +3426,7 @@ pub fn initialize(env: &mut Env) { Obj::Func(b, _) => { let mut acc = Vec::new(); for e in it { - let mut r = b.run(env, vec![e?])?; + let mut r = b.run1(env, e?)?; for k in mut_obj_into_iter(&mut r, "flat_map (inner)")? { acc.push(k?); } @@ -3420,7 +3449,7 @@ pub fn initialize(env: &mut Env) { for a in it { let a = a?; match prev.take() { - Some(prev) => acc.push(b.run(env, vec![prev, a.clone()])?), + Some(prev) => acc.push(b.run2(env, prev, a.clone())?), None => (), }; prev = Some(a); @@ -3485,7 +3514,7 @@ pub fn initialize(env: &mut Env) { let mut acc_f = Vec::new(); for e in it { let e = e?; - if b.run(env, vec![e.clone()])?.truthy() { + if b.run1(env, e.clone())?.truthy() { acc_t.push(e) } else { acc_f.push(e) @@ -4081,7 +4110,10 @@ pub fn initialize(env: &mut Env) { } }, }); - env.insert_builtin(IdBuiltin); + env.insert_builtin(OneArgBuiltin { + name: "id".to_string(), + body: |a| Ok(a), + }); env.insert_builtin(TwoArgBuiltin { name: "const".to_string(), body: |_, b| Ok(b), @@ -4194,7 +4226,7 @@ pub fn initialize(env: &mut Env) { let mut it = mut_obj_into_iter(&mut a, "find")?; while let Some(x) = it.next() { let x = x?; - if f.run(env, vec![x.clone()])?.truthy() { + if f.run1(env, x.clone())?.truthy() { return Ok(x); } } @@ -4210,7 +4242,7 @@ pub fn initialize(env: &mut Env) { let mut it = mut_obj_into_iter(&mut a, "find?")?; while let Some(x) = it.next() { let x = x?; - if f.run(env, vec![x.clone()])?.truthy() { + if f.run1(env, x.clone())?.truthy() { return Ok(x); } } @@ -4225,7 +4257,7 @@ pub fn initialize(env: &mut Env) { (mut a, Obj::Func(f, _)) => { let mut it = mut_obj_into_iter(&mut a, "locate")?.enumerate(); while let Some((i, x)) = it.next() { - if f.run(env, vec![x?.clone()])?.truthy() { + if f.run1(env, x?.clone())?.truthy() { return Ok(Obj::from(i)); } } @@ -4255,7 +4287,7 @@ pub fn initialize(env: &mut Env) { (mut a, Obj::Func(f, _)) => { let mut it = mut_obj_into_iter(&mut a, "locate?")?.enumerate(); while let Some((i, x)) = it.next() { - if f.run(env, vec![x?.clone()])?.truthy() { + if f.run1(env, x?.clone())?.truthy() { return Ok(Obj::from(i)); } } @@ -4512,7 +4544,7 @@ pub fn initialize(env: &mut Env) { for arg in args { match arg { Obj::Func(f, _) => { - cur = f.run(env, vec![cur])?; + cur = f.run1(env, cur)?; } _ => return Err(NErr::type_error("not callable".to_string())), } @@ -4551,7 +4583,7 @@ pub fn initialize(env: &mut Env) { for arg in args { match arg { Obj::Func(f, _) => { - cur = f.run(env, vec![cur])?; + cur = f.run1(env, cur)?; } _ => return Err(NErr::type_error("not callable".to_string())), } diff --git a/src/streams.rs b/src/streams.rs index d5c216c..b92e8c1 100644 --- a/src/streams.rs +++ b/src/streams.rs @@ -516,7 +516,7 @@ impl Iterator for Iterate { Ok((obj, func, renv)) => { let ret = obj.clone(); let cur = std::mem::take(obj); - match func.run(&renv, vec![cur]) { + match func.run1(&renv, cur) { Ok(nxt) => { *obj = nxt; } @@ -588,7 +588,7 @@ impl Iterator for MappedStream { self.0 = Err(e.clone()); Some(Err(e)) } - Some(Ok(cur)) => match func.run(&renv, vec![cur]) { + Some(Ok(cur)) => match func.run1(&renv, cur) { Ok(nxt) => Some(Ok(nxt)), Err(e) => { self.0 = Err(e.clone()); @@ -614,7 +614,7 @@ impl Stream for MappedStream { fn peek(&self) -> Option> { let (inner, func, renv) = self.0.as_ref().ok()?; match inner.peek()? { - Ok(inxt) => match func.run(&renv, vec![inxt]) { + Ok(inxt) => match func.run1(&renv, inxt) { Ok(nxt) => Some(Ok(nxt)), Err(e) => Some(Err(e.clone())), }, From 3ddddecf06b9a732e6c41017cfa49715ed652719 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Sun, 22 Oct 2023 22:21:26 -0700 Subject: [PATCH 08/23] create the internal stack --- src/core.rs | 27 +++++++++++++++++++++++++++ src/eval.rs | 9 +++++++++ src/lex.rs | 5 +++++ 3 files changed, 41 insertions(+) diff --git a/src/core.rs b/src/core.rs index 56a50d7..cae69ee 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1819,6 +1819,10 @@ pub enum Expr { // shouldn't stay in the tree: CommaSeq(Vec>), Splat(Box), + + // hic sunt dracones + InternalPush(Box), + InternalPop, } impl Expr { @@ -2610,6 +2614,9 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { Expr::Return(e) => Ok(Expr::Return(opt_box_freeze(env, e)?)), Expr::Throw(e) => Ok(Expr::Throw(box_freeze(env, e)?)), Expr::Continue => Ok(Expr::Continue), + + Expr::InternalPush(e) => Ok(Expr::InternalPush(box_freeze(env, e)?)), + Expr::InternalPop => Ok(Expr::InternalPop), }?, }) } @@ -3346,6 +3353,23 @@ impl Parser { Err(self.error_here(format!("bad struct name")))? } } + Token::InternalPush => { + self.advance(); + let s = self.single("internal push")?; + Ok(LocExpr { + start, + end: s.end, + expr: Expr::InternalPush(Box::new(s)), + }) + } + Token::InternalPop => { + self.advance(); + Ok(LocExpr { + start, + end, + expr: Expr::InternalPop, + }) + } _ => Err(self.error_here(format!("atom: Unexpected"))), } } else { @@ -4001,6 +4025,7 @@ impl Debug for TopEnv { pub struct Env { pub vars: HashMap>)>, pub parent: Result>, Rc>>, + pub internal_stack: Vec, } // simple, linear-time, and at least finds when one is a subsequence of the other. pub fn fast_edit_distance(a: &[u8], b: &[u8]) -> usize { @@ -4116,6 +4141,7 @@ impl Env { Env { vars: HashMap::new(), parent: Err(Rc::new(RefCell::new(top))), + internal_stack: Vec::new(), } } // ??? @@ -4130,6 +4156,7 @@ impl Env { Rc::new(RefCell::new(Env { vars: HashMap::new(), parent: Ok(Rc::clone(&env)), + internal_stack: Vec::new(), })) } pub fn mut_top_env(&self, f: impl FnOnce(&mut TopEnv) -> T) -> T { diff --git a/src/eval.rs b/src/eval.rs index 7848bf8..8f8a97b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1267,6 +1267,15 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::Continue => Err(NErr::Continue), Expr::Return(Some(e)) => Err(NErr::Return(evaluate(env, e)?)), Expr::Return(None) => Err(NErr::Return(Obj::Null)), + + Expr::InternalPush(e) => { + let r = evaluate(env, e)?; + try_borrow_mut_nres(env, "internal", "push")?.internal_stack.push(r); + Ok(Obj::Null) + } + Expr::InternalPop => { + try_borrow_mut_nres(env, "internal", "pop")?.internal_stack.pop().ok_or_else(|| NErr::empty_error("internal pop".to_string())) + } } } diff --git a/src/lex.rs b/src/lex.rs index 243c77a..5359411 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -62,6 +62,9 @@ pub enum Token { Literally, Underscore, + InternalPush, + InternalPop, + // strip me before parsing Comment(String), // Newline, @@ -527,6 +530,8 @@ impl<'a> Lexer<'a> { "import" => Token::Import, "literally" => Token::Literally, "_" => Token::Underscore, + "__internal_push" => Token::InternalPush, + "__internal_pop" => Token::InternalPop, _ => Token::Ident(acc), }) } From 0f4e0ca6b1a81b31629988b2742d444cf6ff3e69 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Sun, 22 Oct 2023 23:34:27 -0700 Subject: [PATCH 09/23] add internal peek, for --- src/core.rs | 29 +++++++++++++++++++++++++++++ src/eval.rs | 26 +++++++++++++++++++++++--- src/lex.rs | 14 ++++++++++++++ src/lib.rs | 14 +++++--------- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/core.rs b/src/core.rs index cae69ee..1d1e32e 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1823,6 +1823,9 @@ pub enum Expr { // hic sunt dracones InternalPush(Box), InternalPop, + InternalPeek(usize), + // for each element, push it, then run the body. (do not pop, the body is responsible) + InternalFor(Box, Box), } impl Expr { @@ -2617,6 +2620,11 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { Expr::InternalPush(e) => Ok(Expr::InternalPush(box_freeze(env, e)?)), Expr::InternalPop => Ok(Expr::InternalPop), + Expr::InternalPeek(n) => Ok(Expr::InternalPeek(*n)), + Expr::InternalFor(iteratee, body) => Ok(Expr::InternalFor( + box_freeze(env, iteratee)?, + box_freeze(env, body)?, + )), }?, }) } @@ -3370,6 +3378,27 @@ impl Parser { expr: Expr::InternalPop, }) } + Token::InternalPeek(n) => { + let n = *n; + self.advance(); + Ok(LocExpr { + start, + end, + expr: Expr::InternalPeek(n), + }) + } + Token::InternalFor => { + self.advance(); + self.require(Token::LeftParen, "internal for start".to_string())?; + let iteratee = self.single("internal for iteratee")?; + self.require(Token::RightParen, "internal for end".to_string())?; + let body = self.single("internal for body")?; + Ok(LocExpr { + start, + end: body.end, + expr: Expr::InternalFor(Box::new(iteratee), Box::new(body)), + }) + } _ => Err(self.error_here(format!("atom: Unexpected"))), } } else { diff --git a/src/eval.rs b/src/eval.rs index 8f8a97b..03f7ec6 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1270,11 +1270,31 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::InternalPush(e) => { let r = evaluate(env, e)?; - try_borrow_mut_nres(env, "internal", "push")?.internal_stack.push(r); + try_borrow_mut_nres(env, "internal", "push")? + .internal_stack + .push(r); Ok(Obj::Null) } - Expr::InternalPop => { - try_borrow_mut_nres(env, "internal", "pop")?.internal_stack.pop().ok_or_else(|| NErr::empty_error("internal pop".to_string())) + Expr::InternalPop => try_borrow_mut_nres(env, "internal", "pop")? + .internal_stack + .pop() + .ok_or_else(|| NErr::empty_error("internal pop".to_string())), + Expr::InternalPeek(n) => { + let r = try_borrow_mut_nres(env, "internal", "pop")?; + let s = &r.internal_stack; + let x = s[s.len() - n - 1].clone(); + std::mem::drop(r); + Ok(x) + } + Expr::InternalFor(iteratee, body) => { + let mut itr = evaluate(env, iteratee)?; + for x in mut_obj_into_iter(&mut itr, "internal for iteration")? { + try_borrow_mut_nres(env, "internal", "for push")? + .internal_stack + .push(x?); + evaluate(env, body)?; + } + Ok(Obj::Null) } } } diff --git a/src/lex.rs b/src/lex.rs index 5359411..997e249 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -64,6 +64,8 @@ pub enum Token { InternalPush, InternalPop, + InternalPeek(usize), + InternalFor, // strip me before parsing Comment(String), @@ -532,6 +534,18 @@ impl<'a> Lexer<'a> { "_" => Token::Underscore, "__internal_push" => Token::InternalPush, "__internal_pop" => Token::InternalPop, + "__internal_peek" => Token::InternalPeek(0), + // FIXME... + "__internal_peek_1" => Token::InternalPeek(1), + "__internal_peek_2" => Token::InternalPeek(2), + "__internal_peek_3" => Token::InternalPeek(3), + "__internal_peek_4" => Token::InternalPeek(4), + "__internal_peek_5" => Token::InternalPeek(5), + "__internal_peek_6" => Token::InternalPeek(6), + "__internal_peek_7" => Token::InternalPeek(7), + "__internal_peek_8" => Token::InternalPeek(8), + "__internal_peek_9" => Token::InternalPeek(9), + "__internal_for" => Token::InternalFor, _ => Token::Ident(acc), }) } diff --git a/src/lib.rs b/src/lib.rs index 0a7d074..7cb1875 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,9 +135,7 @@ impl Builtin for Minus { match few2(args) { Few2::Zero => Err(NErr::argument_error("received 0 args".to_string())), Few2::One(s) => expect_nums_and_vectorize_1(|x| Ok(Obj::Num(-x)), s, "unary -"), - Few2::Two(a, b) => { - expect_nums_and_vectorize_2_nums(|a, b| a - b, a, b, "binary -") - } + Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(|a, b| a - b, a, b, "binary -"), Few2::Many(a) => err_add_name(Err(NErr::argument_error_args(&a)), "-"), } } @@ -1797,10 +1795,10 @@ fn expect_nums_and_vectorize_2_nums( match (a, b) { (Obj::Num(a), Obj::Num(b)) => Ok(Obj::Num(body(a, b))), (Obj::Num(a), Obj::Seq(Seq::Vector(mut b))) => Ok(Obj::Seq(Seq::Vector(Rc::new( - RcVecIter::of(&mut b).map(|e| body(a.clone(), e)).collect() + RcVecIter::of(&mut b).map(|e| body(a.clone(), e)).collect(), )))), (Obj::Seq(Seq::Vector(mut a)), Obj::Num(b)) => Ok(Obj::Seq(Seq::Vector(Rc::new( - RcVecIter::of(&mut a).map(|e| body(e, b.clone())).collect() + RcVecIter::of(&mut a).map(|e| body(e, b.clone())).collect(), )))), (Obj::Seq(Seq::Vector(mut a)), Obj::Seq(Seq::Vector(mut b))) => { if a.len() == b.len() { @@ -1808,7 +1806,7 @@ fn expect_nums_and_vectorize_2_nums( RcVecIter::of(&mut a) .zip(RcVecIter::of(&mut b)) .map(|(a, b)| body(a, b)) - .collect() + .collect(), )))) } else { Err(NErr::value_error(format!( @@ -2812,9 +2810,7 @@ pub fn initialize(env: &mut Env) { body: |_env, args| match few2(args) { Few2::Zero => Err(NErr::argument_error("received 0 args".to_string())), Few2::One(s) => expect_nums_and_vectorize_1(|x| Ok(Obj::Num(!x)), s, "unary ~"), - Few2::Two(a, b) => { - expect_nums_and_vectorize_2_nums(|a, b| a ^ b, a, b, "binary ~") - } + Few2::Two(a, b) => expect_nums_and_vectorize_2_nums(|a, b| a ^ b, a, b, "binary ~"), Few2::Many(_) => Err(NErr::argument_error("received >2 args".to_string())), }, }); From b12b3e04bfb7f1c20750f8eaa126dac508582dc7 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 20:19:43 -0700 Subject: [PATCH 10/23] more organized push "context manager", peek/for --- src/core.rs | 186 ++++++++++++++++++++++++++++++++-------------------- src/eval.rs | 70 ++++++++++++++------ src/lex.rs | 16 +---- 3 files changed, 167 insertions(+), 105 deletions(-) diff --git a/src/core.rs b/src/core.rs index 1d1e32e..cdde71a 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1821,11 +1821,10 @@ pub enum Expr { Splat(Box), // hic sunt dracones - InternalPush(Box), - InternalPop, - InternalPeek(usize), - // for each element, push it, then run the body. (do not pop, the body is responsible) - InternalFor(Box, Box), + InternalPush(Vec>, Box), + InternalPeek(usize), // from rear + // for each element, put it at a Peek index then run the body + InternalFor(usize, Box, Box), } impl Expr { @@ -1856,6 +1855,7 @@ pub enum Lvalue { Destructure(Box, Vec>), ChainDestructure(Box, Vec<(Box, Box)>), Literally(Box), + InternalPeek(usize), } impl Lvalue { @@ -1873,6 +1873,7 @@ impl Lvalue { lv.any_literals() || vs.iter().any(|e| e.1.any_literals()) } Lvalue::Literally(_) => true, + Lvalue::InternalPeek(_) => false, } } @@ -1911,6 +1912,7 @@ impl Lvalue { s } Lvalue::Literally(_) => HashSet::new(), + Lvalue::InternalPeek(_) => HashSet::new(), } } } @@ -2085,6 +2087,7 @@ pub fn to_lvalue(expr: LocExpr) -> Result { .collect::, Box)>, ParseError>>()?, )), Expr::Literally(e) => Ok(Lvalue::Literally(e)), + Expr::InternalPeek(i) => Ok(Lvalue::InternalPeek(i)), _ => Err(ParseError::expr( format!("can't to_lvalue"), expr.start, @@ -2269,6 +2272,7 @@ fn freeze_lvalue(env: &mut FreezeEnv, lvalue: &Lvalue) -> NRes { .collect::, Box)>>>()?, )), Lvalue::Literally(e) => Ok(Lvalue::Literally(box_freeze(env, e)?)), + Lvalue::InternalPeek(e) => Ok(Lvalue::InternalPeek(*e)), } } @@ -2618,10 +2622,10 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { Expr::Throw(e) => Ok(Expr::Throw(box_freeze(env, e)?)), Expr::Continue => Ok(Expr::Continue), - Expr::InternalPush(e) => Ok(Expr::InternalPush(box_freeze(env, e)?)), - Expr::InternalPop => Ok(Expr::InternalPop), + Expr::InternalPush(a, b) => Ok(Expr::InternalPush(vec_box_freeze(env, a)?, box_freeze(env, b)?)), Expr::InternalPeek(n) => Ok(Expr::InternalPeek(*n)), - Expr::InternalFor(iteratee, body) => Ok(Expr::InternalFor( + Expr::InternalFor(ix, iteratee, body) => Ok(Expr::InternalFor( + *ix, box_freeze(env, iteratee)?, box_freeze(env, body)?, )), @@ -2827,6 +2831,20 @@ impl Parser { } } + // consuming an int that's not a usize is an error + fn try_consume_usize(&mut self, message: &str) -> Result, ParseError> { + if let Some(LocToken { start: _, end, token: Token::IntLit(i) }) = self.peek_loc_token() { + let us = i + .to_usize() + .ok_or(self.error_here(format!("{}: not usize: {}", message, i)))?; + let end = *end; + self.advance(); + Ok(Some((end, us))) + } else { + Ok(None) + } + } + fn peek_loc(&self) -> CodeLoc { match self.peek_loc_token() { Some(t) => t.start, @@ -2848,7 +2866,7 @@ impl Parser { } } - fn require(&mut self, expected: Token, message: String) -> Result { + fn require(&mut self, expected: Token, message: &str) -> Result { if let Some(end) = self.try_consume(&expected) { // wat Ok(end) @@ -3068,7 +3086,7 @@ impl Parser { Token::LeftParen => { self.advance(); let e = self.expression()?; - self.require(Token::RightParen, "normal parenthesized expr".to_string())?; + self.require(Token::RightParen, "normal parenthesized expr")?; Ok(e) } Token::VLeftParen => { @@ -3082,7 +3100,7 @@ impl Parser { } else { let (exs, _) = self.annotated_comma_separated(false, "vector lit contents")?; - let end = self.require(Token::RightParen, "vector expr".to_string())?; + let end = self.require(Token::RightParen, "vector expr")?; Ok(LocExpr { start, end, @@ -3101,7 +3119,7 @@ impl Parser { } else { let (exs, _) = self.annotated_comma_separated(false, "list lit contents")?; - let end = self.require(Token::RightBracket, "list expr".to_string())?; + let end = self.require(Token::RightBracket, "list expr")?; Ok(LocExpr { start, end, @@ -3117,7 +3135,7 @@ impl Parser { if let Some(_) = self.try_consume(&Token::Colon) { def = Some(Box::new(self.single("default of dict literal")?)); if !self.peek_csc_stopper() { - self.require(Token::Comma, "dict expr".to_string())?; + self.require(Token::Comma, "dict expr")?; } } @@ -3131,10 +3149,10 @@ impl Parser { xs.push((c1, c2)); if !self.peek_csc_stopper() { - self.require(Token::Comma, "dict expr".to_string())?; + self.require(Token::Comma, "dict expr")?; } } - let end = self.require(Token::RightBrace, "dict expr end".to_string())?; + let end = self.require(Token::RightBrace, "dict expr end")?; Ok(LocExpr { start, end, @@ -3144,23 +3162,14 @@ impl Parser { Token::RightParen => Err(self.error_here(format!("atom: unexpected"))), Token::Lambda => { self.advance(); - match self.peek_loc_token() { - Some(LocToken { - token: Token::IntLit(x), - start: _, + if let Some((end, us)) = self.try_consume_usize("backref")? { + self.advance(); + Ok(LocExpr { + start, end, - }) => { - let us = x - .to_usize() - .ok_or(self.error_here(format!("backref: not usize: {}", x)))?; - let end = *end; - self.advance(); - Ok(LocExpr { - start, - end, - expr: Expr::Backref(us), - }) - } + expr: Expr::Backref(us), + }) + } else { match self.peek_loc_token() { Some(LocToken { token: Token::Switch, start: switch_start, @@ -3180,7 +3189,7 @@ impl Parser { while let Some(_) = self.try_consume(&Token::Case) { let pat = to_lvalue(self.annotated_pattern(true, "lambda-switch pat")?)?; - self.require(Token::RightArrow, "lambda-case mid: ->".to_string())?; + self.require(Token::RightArrow, "lambda-case mid: ->")?; let res = self.single("lambda-switch body")?; v.push((Box::new(pat), Box::new(res))); } @@ -3216,7 +3225,7 @@ impl Parser { .into_iter() .map(|p| Ok(Box::new(to_lvalue_no_literals(*p)?))) .collect::>, ParseError>>()?; - self.require(Token::RightArrow, "lambda start: ->".to_string())?; + self.require(Token::RightArrow, "lambda start: ->")?; ps }; let body = self.single("body of lambda")?; @@ -3227,12 +3236,13 @@ impl Parser { }) } } + } } Token::If => { self.advance(); - self.require(Token::LeftParen, "if start".to_string())?; + self.require(Token::LeftParen, "if start")?; let cond = self.expression()?; - self.require(Token::RightParen, "if cond end".to_string())?; + self.require(Token::RightParen, "if cond end")?; let body = self.assignment()?; let (end, else_body) = if let Some(end) = self.try_consume(&Token::Else) { (end, Some(Box::new(self.assignment()?))) @@ -3247,7 +3257,7 @@ impl Parser { } Token::For => { self.advance(); - self.require(Token::LeftParen, "for start".to_string())?; + self.require(Token::LeftParen, "for start")?; let mut its = Vec::new(); loop { its.push(self.for_iteration()?); @@ -3287,9 +3297,9 @@ impl Parser { } Token::While => { self.advance(); - self.require(Token::LeftParen, "while start".to_string())?; + self.require(Token::LeftParen, "while start")?; let cond = self.expression()?; - self.require(Token::RightParen, "while end".to_string())?; + self.require(Token::RightParen, "while end")?; let body = self.assignment()?; Ok(LocExpr { start, @@ -3299,13 +3309,13 @@ impl Parser { } Token::Switch => { self.advance(); - self.require(Token::LeftParen, "switch start".to_string())?; + self.require(Token::LeftParen, "switch start")?; let scrutinee = self.expression()?; - self.require(Token::RightParen, "switch end".to_string())?; + self.require(Token::RightParen, "switch end")?; let mut v = Vec::new(); while let Some(_) = self.try_consume(&Token::Case) { let pat = to_lvalue(self.annotated_pattern(true, "switch pat")?)?; - self.require(Token::RightArrow, "case mid: ->".to_string())?; + self.require(Token::RightArrow, "case mid: ->")?; let res = self.single("switch body")?; v.push((Box::new(pat), Box::new(res))); } @@ -3321,9 +3331,9 @@ impl Parser { Token::Try => { self.advance(); let body = self.expression()?; - self.require(Token::Catch, "try end".to_string())?; + self.require(Token::Catch, "try end")?; let pat = to_lvalue(self.annotated_pattern(true, "catch pattern")?)?; - self.require(Token::RightArrow, "catch mid: ->".to_string())?; + self.require(Token::RightArrow, "catch mid: ->")?; let catcher = self.single("catch body")?; Ok(LocExpr { start, @@ -3336,7 +3346,7 @@ impl Parser { if let Some(Token::Ident(name)) = self.peek() { let name = Rc::new(name.clone()); self.advance(); - self.require(Token::LeftParen, "struct begin".to_string())?; + self.require(Token::LeftParen, "struct begin")?; let mut fields = Vec::new(); if let Some(Token::Ident(field1)) = self.peek() { fields.push(Rc::new(field1.clone())); @@ -3350,7 +3360,7 @@ impl Parser { } } } - let end = self.require(Token::RightParen, "struct end".to_string())?; + let end = self.require(Token::RightParen, "struct end")?; Ok(LocExpr { expr: Expr::Struct(name, fields), @@ -3363,40 +3373,59 @@ impl Parser { } Token::InternalPush => { self.advance(); - let s = self.single("internal push")?; + let args = if let Some((end, n)) = self.try_consume_usize("internal push")? { + let mut v = Vec::with_capacity(n); + v.resize_with(n, || Box::new(LocExpr { start: end, end, expr: Expr::Null })); + v + } else { + self.require(Token::LeftParen, "internal push start")?; + let v = self.annotated_comma_separated(false, "internal push args")?.0; + self.require(Token::RightParen, "internal push end")?; + v + }; + let s = self.single("internal push body")?; Ok(LocExpr { start, end: s.end, - expr: Expr::InternalPush(Box::new(s)), + expr: Expr::InternalPush(args, Box::new(s)), }) } - Token::InternalPop => { + Token::InternalPeek => { self.advance(); - Ok(LocExpr { - start, - end, - expr: Expr::InternalPop, - }) - } - Token::InternalPeek(n) => { - let n = *n; - self.advance(); - Ok(LocExpr { - start, - end, - expr: Expr::InternalPeek(n), - }) + if let Some((end, n)) = self.try_consume_usize("internal peek")? { + Ok(LocExpr { + start, + end, + expr: Expr::InternalPeek(n), + }) + } else { + Err(self.error_here(format!("internal peek no n")))? + } } Token::InternalFor => { self.advance(); - self.require(Token::LeftParen, "internal for start".to_string())?; + self.require(Token::LeftParen, "internal for start")?; + let i = if let Some(LocToken { start: _, end: _, token: Token::IntLit(i) }) = self.peek_loc_token() { + match i.to_usize() { + Some(i) => { + self.advance(); + i + } + None => { + return Err(self.error_here(format!("internal for: int too large"))); + } + } + } else { + return Err(self.error_here(format!("internal for: need int"))); + }; + self.require(Token::LeftArrow, "internal for left arrow")?; let iteratee = self.single("internal for iteratee")?; - self.require(Token::RightParen, "internal for end".to_string())?; + self.require(Token::RightParen, "internal for end")?; let body = self.single("internal for body")?; Ok(LocExpr { start, end: body.end, - expr: Expr::InternalFor(Box::new(iteratee), Box::new(body)), + expr: Expr::InternalFor(i, Box::new(iteratee), Box::new(body)), }) } _ => Err(self.error_here(format!("atom: Unexpected"))), @@ -3457,7 +3486,7 @@ impl Parser { }; } else { let (cs, _) = self.annotated_comma_separated(false, "call arg list")?; - let end = self.require(Token::RightParen, "call expr".to_string())?; + let end = self.require(Token::RightParen, "call expr")?; cur = LocExpr { expr: Expr::Call(Box::new(cur), cs, CallSyntax::Parentheses), start, @@ -3477,7 +3506,7 @@ impl Parser { } else { let c = self.single("slice end")?; let end = - self.require(Token::RightBracket, "index expr".to_string())?; + self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index( Box::new(cur), @@ -3502,7 +3531,7 @@ impl Parser { } else { let cc = self.single("slice end")?; let end = - self.require(Token::RightBracket, "index expr".to_string())?; + self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index( Box::new(cur), @@ -3514,7 +3543,7 @@ impl Parser { } } else { let end = - self.require(Token::RightBracket, "index expr".to_string())?; + self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index(Box::new(cur), IndexOrSlice::Index(Box::new(c))), start, @@ -3842,7 +3871,7 @@ impl Parser { let a = to_lvalue_no_literals(self.single("swap target 1")?)?; self.require( Token::Comma, - format!("swap between lvalues at {}", ag_start_err), + &format!("swap between lvalues at {}", ag_start_err), )?; let bs = self.single("swap target 2")?; let end = bs.end; @@ -4222,6 +4251,22 @@ impl Env { } } + pub fn try_borrow_peek(env: &Rc>, i: usize) -> NRes { + let r = try_borrow_nres(env, "internal", "peek")?; + let s = &r.internal_stack; + let x = s[s.len() - i - 1].clone(); + std::mem::drop(r); + Ok(x) + } + + pub fn try_borrow_set_peek(env: &Rc>, i: usize, x: Obj) -> NRes<()> { + let mut r = try_borrow_mut_nres(env, "internal", "peek set")?; + let s = &mut r.internal_stack; + let n = s.len(); + s[n - i - 1] = x; + Ok(()) + } + pub fn modify_existing_var( &self, key: &str, @@ -4235,6 +4280,7 @@ impl Env { }, } } + pub fn insert(&mut self, key: String, ty: ObjType, val: Obj) -> NRes<()> { match self.vars.entry(key) { std::collections::hash_map::Entry::Occupied(e) => { diff --git a/src/eval.rs b/src/eval.rs index 03f7ec6..db8140d 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -28,6 +28,7 @@ pub enum EvaluatedLvalue { Literal(Obj), Destructure(Rc, Vec>), DestructureStruct(Struct, Vec>), + InternalPeek(usize), } impl Display for EvaluatedLvalue { @@ -78,6 +79,9 @@ impl Display for EvaluatedLvalue { } write!(formatter, ")") } + EvaluatedLvalue::InternalPeek(i) => { + write!(formatter, "__internal_peek({})", i) + } } } } @@ -354,6 +358,7 @@ pub fn eval_lvalue(env: &Rc>, expr: &Lvalue) -> NRes Ok(EvaluatedLvalue::Literal(evaluate(env, e)?)), + Lvalue::InternalPeek(e) => Ok(EvaluatedLvalue::InternalPeek(*e)), } } @@ -715,7 +720,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } Expr::Annotation(s, _) => evaluate(env, s), Expr::Consume(pat) => match eval_lvalue(env, pat)? { - EvaluatedLvalue::IndexedIdent(s, ixs) => try_borrow_nres(env, "env for pop", &s)? + EvaluatedLvalue::IndexedIdent(s, ixs) => try_borrow_nres(env, "env for consume", &s)? .modify_existing_var(&s, |(_type, vv)| { modify_existing_index( &mut *try_borrow_mut_nres(vv, "var for consume", &s)?, @@ -724,6 +729,14 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { ) }) .ok_or(NErr::type_error("failed to consume??".to_string()))?, + EvaluatedLvalue::InternalPeek(i) => { + let mut ptr = try_borrow_mut_nres(env, "env for consume", "peek")?; + let s = &mut ptr.internal_stack; + let n = s.len(); + let r = std::mem::take(&mut s[n - 1 - i]); + std::mem::drop(ptr); + Ok(r) + } _ => Err(NErr::type_error("can't consume, weird pattern".to_string())), }, Expr::Pop(pat) => add_trace( @@ -1268,30 +1281,27 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::Return(Some(e)) => Err(NErr::Return(evaluate(env, e)?)), Expr::Return(None) => Err(NErr::Return(Obj::Null)), - Expr::InternalPush(e) => { - let r = evaluate(env, e)?; + Expr::InternalPush(xs, e) => { + let v = xs.iter().map(|x| evaluate(env, x)).collect::>>()?; + let vn = v.len(); + + // careful to keep stack correct even if things error! try_borrow_mut_nres(env, "internal", "push")? - .internal_stack - .push(r); - Ok(Obj::Null) + .internal_stack.extend(v); + let r = evaluate(env, e); + { + let mut ptr = try_borrow_mut_nres(env, "internal", "pop").expect("internal push: stack is out of sync, unrecoverable"); + let s = &mut ptr.internal_stack; + s.truncate(s.len() - vn); + } + r } - Expr::InternalPop => try_borrow_mut_nres(env, "internal", "pop")? - .internal_stack - .pop() - .ok_or_else(|| NErr::empty_error("internal pop".to_string())), - Expr::InternalPeek(n) => { - let r = try_borrow_mut_nres(env, "internal", "pop")?; - let s = &r.internal_stack; - let x = s[s.len() - n - 1].clone(); - std::mem::drop(r); - Ok(x) - } - Expr::InternalFor(iteratee, body) => { + Expr::InternalPeek(i) => Env::try_borrow_peek(env, *i), + Expr::InternalFor(index, iteratee, body) => { let mut itr = evaluate(env, iteratee)?; for x in mut_obj_into_iter(&mut itr, "internal for iteration")? { - try_borrow_mut_nres(env, "internal", "for push")? - .internal_stack - .push(x?); + let x = x?; + Env::try_borrow_set_peek(env, *index, x)?; evaluate(env, body)?; } Ok(Obj::Null) @@ -1527,6 +1537,9 @@ pub fn eval_lvalue_as_obj(env: &REnv, expr: &EvaluatedLvalue) -> NRes { ))) } } + EvaluatedLvalue::InternalPeek(i) => { + Env::try_borrow_peek(env, *i) + } } } @@ -2169,6 +2182,9 @@ pub fn assign(env: &REnv, lhs: &EvaluatedLvalue, rt: Option<&ObjType>, rhs: Obj) "Destructuring structure failed: not type".to_string(), )), }, + EvaluatedLvalue::InternalPeek(i) => { + Env::try_borrow_set_peek(env, *i, rhs) + }, } } @@ -2220,6 +2236,9 @@ pub fn drop_lhs(env: &REnv, lhs: &EvaluatedLvalue) -> NRes<()> { EvaluatedLvalue::Literal(_) => Ok(()), // assigning to it probably will fail later... EvaluatedLvalue::Destructure(_, vs) => drop_lhs_all(env, vs), EvaluatedLvalue::DestructureStruct(_, vs) => drop_lhs_all(env, vs), + EvaluatedLvalue::InternalPeek(i) => { + Env::try_borrow_set_peek(env, *i, Obj::Null) + }, } } @@ -2299,6 +2318,9 @@ pub fn assign_every(env: &REnv, lhs: &EvaluatedLvalue, rt: Option<&ObjType>, rhs "Can't assign-every to struct {:?}", lhs ))), + EvaluatedLvalue::InternalPeek(i) => { + Env::try_borrow_set_peek(env, *i, rhs) + } } } @@ -2370,6 +2392,12 @@ pub fn modify_every( "Can't modify destructure struct {:?}", lhs ))), + EvaluatedLvalue::InternalPeek(i) => { + let old = Env::try_borrow_peek(env, *i)?; + let new = rhs(old)?; + Env::try_borrow_set_peek(env, *i, new)?; + Ok(()) + } } } diff --git a/src/lex.rs b/src/lex.rs index 997e249..5e7da6f 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -63,8 +63,7 @@ pub enum Token { Underscore, InternalPush, - InternalPop, - InternalPeek(usize), + InternalPeek, InternalFor, // strip me before parsing @@ -533,18 +532,7 @@ impl<'a> Lexer<'a> { "literally" => Token::Literally, "_" => Token::Underscore, "__internal_push" => Token::InternalPush, - "__internal_pop" => Token::InternalPop, - "__internal_peek" => Token::InternalPeek(0), - // FIXME... - "__internal_peek_1" => Token::InternalPeek(1), - "__internal_peek_2" => Token::InternalPeek(2), - "__internal_peek_3" => Token::InternalPeek(3), - "__internal_peek_4" => Token::InternalPeek(4), - "__internal_peek_5" => Token::InternalPeek(5), - "__internal_peek_6" => Token::InternalPeek(6), - "__internal_peek_7" => Token::InternalPeek(7), - "__internal_peek_8" => Token::InternalPeek(8), - "__internal_peek_9" => Token::InternalPeek(9), + "__internal_peek" => Token::InternalPeek, "__internal_for" => Token::InternalFor, _ => Token::Ident(acc), }) From 601dde6bd7648b20beb5f5ad3cb521005bbd8c74 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 20:57:07 -0700 Subject: [PATCH 11/23] nvm managed pushing/popping is fine --- src/core.rs | 62 +++++++++++++++++++++++++---------------------------- src/eval.rs | 48 +++++++++++++++++++++++++++++------------ src/lex.rs | 4 ++++ 3 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/core.rs b/src/core.rs index cdde71a..a2c9512 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1821,10 +1821,12 @@ pub enum Expr { Splat(Box), // hic sunt dracones - InternalPush(Vec>, Box), + InternalFrame(Box), + InternalPush(Box), + InternalPop, InternalPeek(usize), // from rear - // for each element, put it at a Peek index then run the body - InternalFor(usize, Box, Box), + // for each element, push it, run the body, then restore stack length + InternalFor(Box, Box), } impl Expr { @@ -2622,10 +2624,11 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { Expr::Throw(e) => Ok(Expr::Throw(box_freeze(env, e)?)), Expr::Continue => Ok(Expr::Continue), - Expr::InternalPush(a, b) => Ok(Expr::InternalPush(vec_box_freeze(env, a)?, box_freeze(env, b)?)), + Expr::InternalFrame(e) => Ok(Expr::InternalFrame(box_freeze(env, e)?)), + Expr::InternalPush(e) => Ok(Expr::InternalPush(box_freeze(env, e)?)), + Expr::InternalPop => Ok(Expr::InternalPop), Expr::InternalPeek(n) => Ok(Expr::InternalPeek(*n)), - Expr::InternalFor(ix, iteratee, body) => Ok(Expr::InternalFor( - *ix, + Expr::InternalFor(iteratee, body) => Ok(Expr::InternalFor( box_freeze(env, iteratee)?, box_freeze(env, body)?, )), @@ -3371,23 +3374,30 @@ impl Parser { Err(self.error_here(format!("bad struct name")))? } } + Token::InternalFrame => { + self.advance(); + let s = self.single("internal frame")?; + Ok(LocExpr { + start, + end: s.end, + expr: Expr::InternalFrame(Box::new(s)), + }) + } Token::InternalPush => { self.advance(); - let args = if let Some((end, n)) = self.try_consume_usize("internal push")? { - let mut v = Vec::with_capacity(n); - v.resize_with(n, || Box::new(LocExpr { start: end, end, expr: Expr::Null })); - v - } else { - self.require(Token::LeftParen, "internal push start")?; - let v = self.annotated_comma_separated(false, "internal push args")?.0; - self.require(Token::RightParen, "internal push end")?; - v - }; - let s = self.single("internal push body")?; + let s = self.single("internal push")?; Ok(LocExpr { start, end: s.end, - expr: Expr::InternalPush(args, Box::new(s)), + expr: Expr::InternalPush(Box::new(s)), + }) + } + Token::InternalPop => { + self.advance(); + Ok(LocExpr { + start, + end, + expr: Expr::InternalPop, }) } Token::InternalPeek => { @@ -3405,27 +3415,13 @@ impl Parser { Token::InternalFor => { self.advance(); self.require(Token::LeftParen, "internal for start")?; - let i = if let Some(LocToken { start: _, end: _, token: Token::IntLit(i) }) = self.peek_loc_token() { - match i.to_usize() { - Some(i) => { - self.advance(); - i - } - None => { - return Err(self.error_here(format!("internal for: int too large"))); - } - } - } else { - return Err(self.error_here(format!("internal for: need int"))); - }; - self.require(Token::LeftArrow, "internal for left arrow")?; let iteratee = self.single("internal for iteratee")?; self.require(Token::RightParen, "internal for end")?; let body = self.single("internal for body")?; Ok(LocExpr { start, end: body.end, - expr: Expr::InternalFor(i, Box::new(iteratee), Box::new(body)), + expr: Expr::InternalFor(Box::new(iteratee), Box::new(body)), }) } _ => Err(self.error_here(format!("atom: Unexpected"))), diff --git a/src/eval.rs b/src/eval.rs index db8140d..336d2c7 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1281,28 +1281,48 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::Return(Some(e)) => Err(NErr::Return(evaluate(env, e)?)), Expr::Return(None) => Err(NErr::Return(Obj::Null)), - Expr::InternalPush(xs, e) => { - let v = xs.iter().map(|x| evaluate(env, x)).collect::>>()?; - let vn = v.len(); - - // careful to keep stack correct even if things error! + Expr::InternalPush(e) => { + let r = evaluate(env, e)?; try_borrow_mut_nres(env, "internal", "push")? - .internal_stack.extend(v); + .internal_stack + .push(r); + Ok(Obj::Null) + } + Expr::InternalPop => try_borrow_mut_nres(env, "internal", "pop")? + .internal_stack + .pop() + .ok_or_else(|| NErr::empty_error("internal pop".to_string())), + Expr::InternalFrame(e) => { + let n = try_borrow_mut_nres(env, "internal", "frame start")?.internal_stack.len(); let r = evaluate(env, e); - { - let mut ptr = try_borrow_mut_nres(env, "internal", "pop").expect("internal push: stack is out of sync, unrecoverable"); - let s = &mut ptr.internal_stack; - s.truncate(s.len() - vn); - } + try_borrow_mut_nres(env, "internal", "frame end").expect("internal frame end: stack is out of sync, unrecoverable").internal_stack + .truncate(n); r } Expr::InternalPeek(i) => Env::try_borrow_peek(env, *i), - Expr::InternalFor(index, iteratee, body) => { + Expr::InternalFor(iteratee, body) => { let mut itr = evaluate(env, iteratee)?; for x in mut_obj_into_iter(&mut itr, "internal for iteration")? { let x = x?; - Env::try_borrow_set_peek(env, *index, x)?; - evaluate(env, body)?; + let s = { + let mut ptr = try_borrow_mut_nres(env, "internal", "for push")?; + let s = ptr.internal_stack.len(); + ptr.internal_stack.push(x); + s + }; + let ret = evaluate(env, body); + { + let mut ptr = try_borrow_mut_nres(env, "internal", "for push").expect("internal for: stack is out of sync, unrecoverable"); + ptr.internal_stack.truncate(s); + } + match ret { + Err(NErr::Break(k)) => { + return Ok(k.unwrap_or(Obj::Null)) + } + Err(NErr::Continue) => (), + Err(r) => return Err(r), + Ok(_) => (), + } } Ok(Obj::Null) } diff --git a/src/lex.rs b/src/lex.rs index 5e7da6f..62b6bd8 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -62,7 +62,9 @@ pub enum Token { Literally, Underscore, + InternalFrame, InternalPush, + InternalPop, InternalPeek, InternalFor, @@ -531,7 +533,9 @@ impl<'a> Lexer<'a> { "import" => Token::Import, "literally" => Token::Literally, "_" => Token::Underscore, + "__internal_frame" => Token::InternalFrame, "__internal_push" => Token::InternalPush, + "__internal_pop" => Token::InternalPop, "__internal_peek" => Token::InternalPeek, "__internal_for" => Token::InternalFor, _ => Token::Ident(acc), From 8ed54ec2467c54b5c9cfee5e529659687b679dd9 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 21:22:29 -0700 Subject: [PATCH 12/23] frameless lambdas --- src/core.rs | 17 +++++++++++++++++ src/eval.rs | 24 ++++++++++++++++++++++++ src/lex.rs | 2 ++ 3 files changed, 43 insertions(+) diff --git a/src/core.rs b/src/core.rs index a2c9512..b5a19bf 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1827,6 +1827,7 @@ pub enum Expr { InternalPeek(usize), // from rear // for each element, push it, run the body, then restore stack length InternalFor(Box, Box), + InternalLambda(Rc), } impl Expr { @@ -2632,6 +2633,11 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { box_freeze(env, iteratee)?, box_freeze(env, body)?, )), + Expr::InternalLambda(body) => { + Ok(Expr::InternalLambda( + Rc::new(freeze(env, body)?), + )) + } }?, }) } @@ -3424,6 +3430,15 @@ impl Parser { expr: Expr::InternalFor(Box::new(iteratee), Box::new(body)), }) } + Token::InternalLambda => { + self.advance(); + let s = self.single("internal lambda")?; + Ok(LocExpr { + start, + end: s.end, + expr: Expr::InternalLambda(Rc::new(s)), + }) + } _ => Err(self.error_here(format!("atom: Unexpected"))), } } else { @@ -4003,6 +4018,7 @@ pub fn parse(code: &str) -> Result, ParseError> { pub enum Func { Builtin(Rc), Closure(Closure), + InternalLambda(Rc), // partially applied first argument (lower priority) PartialApp1(Box, Box), // partially applied second argument (more of the default in our weird world) @@ -4331,6 +4347,7 @@ impl Display for Func { match self { Func::Builtin(b) => write!(formatter, "Builtin({})", b.builtin_name()), Func::Closure(_) => write!(formatter, "Closure"), + Func::InternalLambda(_) => write!(formatter, "InternalLambda"), Func::PartialApp1(f, x) => write!(formatter, "Partial({}({}, ?))", f, FmtObj::debug(x)), Func::PartialApp2(f, x) => write!(formatter, "Partial({}(?, {}))", f, FmtObj::debug(x)), Func::PartialAppLast(f, x) => { diff --git a/src/eval.rs b/src/eval.rs index 336d2c7..f317b81 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1326,6 +1326,10 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } Ok(Obj::Null) } + Expr::InternalLambda(body) => Ok(Obj::Func( + Func::InternalLambda(Rc::clone(body)), + Precedence::zero(), + )), } } @@ -2426,6 +2430,25 @@ impl Func { match self { Func::Builtin(b) => b.run(env, args), Func::Closure(c) => c.run(args), + Func::InternalLambda(body) => { + let s = { + let mut ptr = try_borrow_mut_nres(env, "internal", "lambda call")?; + let n = ptr.internal_stack.len(); + ptr.internal_stack.extend(args); + n + }; + let ret = match evaluate(env, body) { + Err(NErr::Return(k)) => Ok(k), + x => add_trace( + x, + || "closure call".to_string(), + body.start, + body.end, + ), + }; + try_borrow_mut_nres(env, "internal", "lambda call")?.internal_stack.truncate(s); + ret + } Func::PartialApp1(f, x) => match few(args) { Few::One(arg) => f.run2(env, (**x).clone(), arg), a => Err(NErr::argument_error(format!("partially applied functions can only be called with one more argument, but {} {} got {}", f, FmtObj::debug(x), a))) @@ -2623,6 +2646,7 @@ impl Func { Func::Type(_) => None, Func::StructField(..) => None, Func::Memoized(..) => None, + Func::InternalLambda(_) => None, } } } diff --git a/src/lex.rs b/src/lex.rs index 62b6bd8..ec7a245 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -67,6 +67,7 @@ pub enum Token { InternalPop, InternalPeek, InternalFor, + InternalLambda, // strip me before parsing Comment(String), @@ -538,6 +539,7 @@ impl<'a> Lexer<'a> { "__internal_pop" => Token::InternalPop, "__internal_peek" => Token::InternalPeek, "__internal_for" => Token::InternalFor, + "__internal_lambda" => Token::InternalLambda, _ => Token::Ident(acc), }) } From 1ffbe8c3025757abea14a206162dc016c9859076 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 21:43:06 -0700 Subject: [PATCH 13/23] internal call --- src/core.rs | 15 +++++++++++++++ src/eval.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/lex.rs | 2 ++ 3 files changed, 55 insertions(+) diff --git a/src/core.rs b/src/core.rs index b5a19bf..cb50d02 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1827,6 +1827,7 @@ pub enum Expr { InternalPeek(usize), // from rear // for each element, push it, run the body, then restore stack length InternalFor(Box, Box), + InternalCall(usize, Box), InternalLambda(Rc), } @@ -2633,6 +2634,7 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { box_freeze(env, iteratee)?, box_freeze(env, body)?, )), + Expr::InternalCall(argc, e) => Ok(Expr::InternalCall(*argc, box_freeze(env, e)?)), Expr::InternalLambda(body) => { Ok(Expr::InternalLambda( Rc::new(freeze(env, body)?), @@ -3430,6 +3432,19 @@ impl Parser { expr: Expr::InternalFor(Box::new(iteratee), Box::new(body)), }) } + Token::InternalCall => { + self.advance(); + if let Some((end, n)) = self.try_consume_usize("internal call")? { + let body = self.single("internal call body")?; + Ok(LocExpr { + start, + end, + expr: Expr::InternalCall(n, Box::new(body)), + }) + } else { + Err(self.error_here(format!("internal call no n")))? + } + } Token::InternalLambda => { self.advance(); let s = self.single("internal lambda")?; diff --git a/src/eval.rs b/src/eval.rs index f317b81..10cf957 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1326,6 +1326,34 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } Ok(Obj::Null) } + Expr::InternalCall(1, e) => { + let val = evaluate(env, e)?; + let arg = { + let mut ptr = try_borrow_mut_nres(env, "internal", "call")?; + ptr.internal_stack.pop().expect("internal call 1") + }; + call1(env, val, arg) + } + Expr::InternalCall(2, e) => { + let val = evaluate(env, e)?; + let (arg2, arg1) = { + let mut ptr = try_borrow_mut_nres(env, "internal", "call")?; + ( + ptr.internal_stack.pop().expect("internal call 2"), + ptr.internal_stack.pop().expect("internal call 1"), + ) + }; + call2(env, val, arg1, arg2) + } + Expr::InternalCall(argc, e) => { + let val = evaluate(env, e)?; + let args = { + let mut ptr = try_borrow_mut_nres(env, "internal", "call")?; + let n = ptr.internal_stack.len(); + ptr.internal_stack.split_off(n - argc) + }; + call(env, val, args) + } Expr::InternalLambda(body) => Ok(Obj::Func( Func::InternalLambda(Rc::clone(body)), Precedence::zero(), @@ -1577,6 +1605,16 @@ pub fn call1(env: &REnv, f: Obj, arg: Obj) -> NRes { } } +pub fn call2(env: &REnv, f: Obj, arg1: Obj, arg2: Obj) -> NRes { + match f { + Obj::Func(ff, _) => ff.run2(env, arg1, arg2), + _ => Err(NErr::type_error(format!( + "Can't call non-function {}", + FmtObj::debug(&f) + ))), + } +} + pub fn call(env: &REnv, f: Obj, args: Vec) -> NRes { match f { Obj::Func(ff, _) => ff.run(env, args), diff --git a/src/lex.rs b/src/lex.rs index ec7a245..dc10ff6 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -67,6 +67,7 @@ pub enum Token { InternalPop, InternalPeek, InternalFor, + InternalCall, InternalLambda, // strip me before parsing @@ -539,6 +540,7 @@ impl<'a> Lexer<'a> { "__internal_pop" => Token::InternalPop, "__internal_peek" => Token::InternalPeek, "__internal_for" => Token::InternalFor, + "__internal_call" => Token::InternalCall, "__internal_lambda" => Token::InternalLambda, _ => Token::Ident(acc), }) From a9d1920c36bcdbd63ccbb7e6f7e2b4fe6dbfd6a8 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 23 Oct 2023 21:48:54 -0700 Subject: [PATCH 14/23] indulgence --- src/lex.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lex.rs b/src/lex.rs index dc10ff6..1af8dc6 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -437,8 +437,12 @@ impl<'a> Lexer<'a> { _ => self.emit(Token::IntLit(acc.parse::().unwrap())), } } - } else if c.is_alphabetic() || c == '_' { - let mut acc = c.to_string(); + } else if c.is_alphabetic() || c == '_' || c == '🐉' { + let mut acc = if c == '🐉' { + "__internal_".to_string() + } else { + c.to_string() + }; while let Some(cc) = self.peek().filter(|c| { c.is_alphanumeric() || **c == '_' || **c == '\'' || **c == '?' From cb5a6e8327ef50bab654425ae087bf593eb87f2d Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Tue, 24 Oct 2023 00:34:37 -0700 Subject: [PATCH 15/23] small int optimization --- src/core.rs | 215 ++++++++++++----------- src/eval.rs | 35 ++-- src/lib.rs | 123 +++++++------- src/nint.rs | 452 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nnum.rs | 182 +++++++++----------- src/streams.rs | 12 +- tests/test.rs | 22 ++- 7 files changed, 748 insertions(+), 293 deletions(-) create mode 100644 src/nint.rs diff --git a/src/core.rs b/src/core.rs index cb50d02..da47d5d 100644 --- a/src/core.rs +++ b/src/core.rs @@ -21,6 +21,7 @@ use crate::few::*; use crate::iter::*; use crate::lex::*; +use crate::nint::NInt; use crate::nnum::NNum; // The fundamental unitype and all its types... @@ -273,7 +274,7 @@ impl Iterator for ObjToCloningIter<'_> { ObjToCloningIter::Dict(it) => Some(Ok(key_to_obj(it.next()?.0.clone()))), ObjToCloningIter::String(it) => Some(Ok(Obj::from(it.next()?))), ObjToCloningIter::Vector(it) => Some(Ok(Obj::Num(it.next()?.clone()))), - ObjToCloningIter::Bytes(it) => Some(Ok(Obj::from(*it.next()? as usize))), + ObjToCloningIter::Bytes(it) => Some(Ok(Obj::u8(*it.next()?))), ObjToCloningIter::Stream(it) => it.next(), } } @@ -344,7 +345,7 @@ impl Iterator for MutObjIntoIter<'_> { MutObjIntoIter::Dict(it) => Some(Ok(key_to_obj(it.next()?.0))), MutObjIntoIter::String(it) => Some(Ok(Obj::from(it.next()?))), MutObjIntoIter::Vector(it) => Some(Ok(Obj::Num(it.next()?.clone()))), - MutObjIntoIter::Bytes(it) => Some(Ok(Obj::from(it.next()? as usize))), + MutObjIntoIter::Bytes(it) => Some(Ok(Obj::u8(it.next()?))), MutObjIntoIter::Stream(it) => match Rc::get_mut(it) { Some(it) => it.next(), None => { @@ -407,7 +408,7 @@ impl Iterator for MutObjIntoIterPairs<'_> { let o = it.next()?; let j = *i; *i += 1; - Some(Ok((ObjKey::from(j), Obj::from(o as usize)))) + Some(Ok((ObjKey::from(j), Obj::u8(o)))) } MutObjIntoIterPairs::Stream(i, s) => { let o = match Rc::get_mut(s) { @@ -551,7 +552,11 @@ pub fn type_of(obj: &Obj) -> ObjType { pub fn expect_one(args: Vec, msg: &str) -> NRes { match few(args) { Few::One(arg) => Ok(arg), - t => Err(NErr::type_error(format!("{} expected one argument, got {}", msg, t.len()))), + t => Err(NErr::type_error(format!( + "{} expected one argument, got {}", + msg, + t.len() + ))), } } @@ -633,13 +638,8 @@ pub fn call_type1(ty: &ObjType, arg: Obj) -> NRes { None, )), }, - ObjType::Type => Ok(Obj::Func( - Func::Type(type_of(&arg)), - Precedence::zero(), - )), - ObjType::Struct(_) => { - call_type(ty, vec![arg]) - } + ObjType::Type => Ok(Obj::Func(Func::Type(type_of(&arg)), Precedence::zero())), + ObjType::Struct(_) => call_type(ty, vec![arg]), // TODO: complex _ => Err(NErr::type_error( "that type can't be called (maybe not implemented)".to_string(), @@ -737,9 +737,10 @@ impl From for Obj { Obj::Num(NNum::iverson(n)) } } +// we need this one for the fake "higher-ranked" types using Bytes impl From for Obj { fn from(n: u8) -> Self { - Obj::Num(NNum::Int(BigInt::from(n))) + Obj::Num(NNum::u8(n)) } } impl From for Obj { @@ -774,15 +775,16 @@ macro_rules! forward_from { } }; } +forward_from!(NInt); forward_from!(BigInt); // forward_from!(i32); forward_from!(f64); -forward_from!(usize); +// forward_from!(usize); forward_from!(Complex64); impl From for ObjKey { fn from(n: usize) -> Self { - ObjKey(Obj::from(n)) + ObjKey(Obj::usize(n)) } } impl From for ObjKey { @@ -815,6 +817,10 @@ pub fn obj_clamp_to_usize_ok(n: &Obj) -> NRes { _ => Err(NErr::type_error(format!("bad scalar {}", FmtObj::debug(n)))), } } +pub fn into_nint_ok(n: NNum) -> NRes { + n.into_nint() + .ok_or(NErr::value_error("bad number to int".to_string())) +} pub fn into_bigint_ok(n: NNum) -> NRes { n.into_bigint() .ok_or(NErr::value_error("bad number to int".to_string())) @@ -986,8 +992,14 @@ impl Obj { } } - pub fn i32(n: i32) -> Self { - Obj::Num(NNum::Int(BigInt::from(n))) + pub fn i64(n: i64) -> Self { + Obj::Num(NNum::Int(NInt::Small(n))) + } + pub fn usize(n: usize) -> Self { + Obj::Num(NNum::usize(n)) + } + pub fn u8(n: u8) -> Self { + Obj::Num(NNum::u8(n)) } pub fn list(n: Vec) -> Self { Obj::Seq(Seq::List(Rc::new(n))) @@ -997,13 +1009,13 @@ impl Obj { } pub fn zero() -> Self { - Obj::Num(NNum::Int(BigInt::from(0))) + Obj::i64(0) } pub fn one() -> Self { - Obj::Num(NNum::Int(BigInt::from(1))) + Obj::i64(1) } pub fn neg_one() -> Self { - Obj::Num(NNum::Int(BigInt::from(-1))) + Obj::i64(-1) } } @@ -1766,6 +1778,7 @@ pub enum CallSyntax { #[derive(Debug)] pub enum Expr { Null, + IntLit64(i64), IntLit(BigInt), RatLit(BigRational), FloatLit(f64), @@ -1824,7 +1837,7 @@ pub enum Expr { InternalFrame(Box), InternalPush(Box), InternalPop, - InternalPeek(usize), // from rear + InternalPeek(usize), // from rear // for each element, push it, run the body, then restore stack length InternalFor(Box, Box), InternalCall(usize, Box), @@ -1835,6 +1848,7 @@ impl Expr { fn constant_value(&self) -> Option { match self { Expr::Null => Some(Obj::Null), + Expr::IntLit64(x) => Some(Obj::from(NInt::Small(*x))), Expr::IntLit(x) => Some(Obj::from(x.clone())), Expr::RatLit(x) => Some(Obj::from(NNum::from(x.clone()))), Expr::FloatLit(x) => Some(Obj::from(*x)), @@ -2071,6 +2085,7 @@ pub fn to_lvalue(expr: LocExpr) -> Result { .collect::>, ParseError>>()?, )), Expr::Splat(x) => Ok(Lvalue::Splat(Box::new(to_lvalue(*x)?))), + Expr::IntLit64(n) => Ok(Lvalue::Literal(Obj::from(NInt::Small(n)))), Expr::IntLit(n) => Ok(Lvalue::Literal(Obj::from(n))), Expr::StringLit(n) => Ok(Lvalue::Literal(Obj::Seq(Seq::String(n)))), Expr::BytesLit(n) => Ok(Lvalue::Literal(Obj::Seq(Seq::Bytes(n)))), @@ -2309,6 +2324,7 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { end: expr.end, expr: match &expr.expr { Expr::Null => Ok(Expr::Null), + Expr::IntLit64(x) => Ok(Expr::IntLit64(*x)), Expr::IntLit(x) => Ok(Expr::IntLit(x.clone())), Expr::RatLit(x) => Ok(Expr::RatLit(x.clone())), Expr::FloatLit(x) => Ok(Expr::FloatLit(x.clone())), @@ -2635,11 +2651,7 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { box_freeze(env, body)?, )), Expr::InternalCall(argc, e) => Ok(Expr::InternalCall(*argc, box_freeze(env, e)?)), - Expr::InternalLambda(body) => { - Ok(Expr::InternalLambda( - Rc::new(freeze(env, body)?), - )) - } + Expr::InternalLambda(body) => Ok(Expr::InternalLambda(Rc::new(freeze(env, body)?))), }?, }) } @@ -2844,7 +2856,12 @@ impl Parser { // consuming an int that's not a usize is an error fn try_consume_usize(&mut self, message: &str) -> Result, ParseError> { - if let Some(LocToken { start: _, end, token: Token::IntLit(i) }) = self.peek_loc_token() { + if let Some(LocToken { + start: _, + end, + token: Token::IntLit(i), + }) = self.peek_loc_token() + { let us = i .to_usize() .ok_or(self.error_here(format!("{}: not usize: {}", message, i)))?; @@ -2906,7 +2923,10 @@ impl Parser { Ok(LocExpr { start, end, - expr: Expr::IntLit(n), + expr: match n.to_i64() { + Some(n) => Expr::IntLit64(n), + None => Expr::IntLit(n), + }, }) } Token::RatLit(n) => { @@ -3180,73 +3200,77 @@ impl Parser { end, expr: Expr::Backref(us), }) - } else { match self.peek_loc_token() { - Some(LocToken { - token: Token::Switch, - start: switch_start, - end: switch_end, - }) => { - // "magic" variable normally inexpressible - let param = Lvalue::IndexedIdent("switch".to_string(), Vec::new()); - let scrutinee = LocExpr { - expr: Expr::Ident("switch".to_string()), - start: *switch_start, - end: *switch_end, - }; - self.advance(); + } else { + match self.peek_loc_token() { + Some(LocToken { + token: Token::Switch, + start: switch_start, + end: switch_end, + }) => { + // "magic" variable normally inexpressible + let param = Lvalue::IndexedIdent("switch".to_string(), Vec::new()); + let scrutinee = LocExpr { + expr: Expr::Ident("switch".to_string()), + start: *switch_start, + end: *switch_end, + }; + self.advance(); - let mut v = Vec::new(); - // FIXME copypasta... - while let Some(_) = self.try_consume(&Token::Case) { - let pat = - to_lvalue(self.annotated_pattern(true, "lambda-switch pat")?)?; - self.require(Token::RightArrow, "lambda-case mid: ->")?; - let res = self.single("lambda-switch body")?; - v.push((Box::new(pat), Box::new(res))); - } - match v.last() { - Some((_, e)) => { - let end = e.end; - Ok(LocExpr { - start, - end: e.end, - expr: Expr::Lambda( - Rc::new(vec![Box::new(param)]), - Rc::new(LocExpr { - expr: Expr::Switch(Box::new(scrutinee), v), - start, - end, - }), - ), - }) + let mut v = Vec::new(); + // FIXME copypasta... + while let Some(_) = self.try_consume(&Token::Case) { + let pat = to_lvalue( + self.annotated_pattern(true, "lambda-switch pat")?, + )?; + self.require(Token::RightArrow, "lambda-case mid: ->")?; + let res = self.single("lambda-switch body")?; + v.push((Box::new(pat), Box::new(res))); + } + match v.last() { + Some((_, e)) => { + let end = e.end; + Ok(LocExpr { + start, + end: e.end, + expr: Expr::Lambda( + Rc::new(vec![Box::new(param)]), + Rc::new(LocExpr { + expr: Expr::Switch(Box::new(scrutinee), v), + start, + end, + }), + ), + }) + } + None => { + Err(self.error_here(format!("lambda-switch: no cases")))? + } } - None => Err(self.error_here(format!("lambda-switch: no cases")))?, + } + _ => { + let params = if self.try_consume(&Token::RightArrow).is_some() { + Vec::new() + } else { + // Hmm. In a lambda, `a` and `a,` are the same, but on the LHS of an + // assignment the second unpacks. + let params0 = + self.annotated_comma_separated(true, "lambda params")?; + let ps = params0 + .0 + .into_iter() + .map(|p| Ok(Box::new(to_lvalue_no_literals(*p)?))) + .collect::>, ParseError>>()?; + self.require(Token::RightArrow, "lambda start: ->")?; + ps + }; + let body = self.single("body of lambda")?; + Ok(LocExpr { + start, + end: body.end, + expr: Expr::Lambda(Rc::new(params), Rc::new(body)), + }) } } - _ => { - let params = if self.try_consume(&Token::RightArrow).is_some() { - Vec::new() - } else { - // Hmm. In a lambda, `a` and `a,` are the same, but on the LHS of an - // assignment the second unpacks. - let params0 = - self.annotated_comma_separated(true, "lambda params")?; - let ps = params0 - .0 - .into_iter() - .map(|p| Ok(Box::new(to_lvalue_no_literals(*p)?))) - .collect::>, ParseError>>()?; - self.require(Token::RightArrow, "lambda start: ->")?; - ps - }; - let body = self.single("body of lambda")?; - Ok(LocExpr { - start, - end: body.end, - expr: Expr::Lambda(Rc::new(params), Rc::new(body)), - }) - } - } } } Token::If => { @@ -3435,7 +3459,7 @@ impl Parser { Token::InternalCall => { self.advance(); if let Some((end, n)) = self.try_consume_usize("internal call")? { - let body = self.single("internal call body")?; + let body = self.single("internal call body")?; Ok(LocExpr { start, end, @@ -3531,8 +3555,7 @@ impl Parser { }; } else { let c = self.single("slice end")?; - let end = - self.require(Token::RightBracket, "index expr")?; + let end = self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index( Box::new(cur), @@ -3556,8 +3579,7 @@ impl Parser { }; } else { let cc = self.single("slice end")?; - let end = - self.require(Token::RightBracket, "index expr")?; + let end = self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index( Box::new(cur), @@ -3568,8 +3590,7 @@ impl Parser { }; } } else { - let end = - self.require(Token::RightBracket, "index expr")?; + let end = self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index(Box::new(cur), IndexOrSlice::Index(Box::new(c))), start, diff --git a/src/eval.rs b/src/eval.rs index 10cf957..97dc104 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -2,6 +2,7 @@ use crate::core::*; use crate::few::*; use crate::iter::*; use crate::lex::*; +use crate::nint::NInt; use crate::nnum::NNum; use num::complex::Complex64; use std::cell::RefCell; @@ -488,6 +489,7 @@ fn splat_section_eval( pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { match &expr.expr { Expr::Null => Ok(Obj::Null), + Expr::IntLit64(n) => Ok(Obj::from(NInt::Small(*n))), Expr::IntLit(n) => Ok(Obj::from(n.clone())), Expr::RatLit(n) => Ok(Obj::from(NNum::from(n.clone()))), Expr::FloatLit(n) => Ok(Obj::from(*n)), @@ -1293,9 +1295,13 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { .pop() .ok_or_else(|| NErr::empty_error("internal pop".to_string())), Expr::InternalFrame(e) => { - let n = try_borrow_mut_nres(env, "internal", "frame start")?.internal_stack.len(); + let n = try_borrow_mut_nres(env, "internal", "frame start")? + .internal_stack + .len(); let r = evaluate(env, e); - try_borrow_mut_nres(env, "internal", "frame end").expect("internal frame end: stack is out of sync, unrecoverable").internal_stack + try_borrow_mut_nres(env, "internal", "frame end") + .expect("internal frame end: stack is out of sync, unrecoverable") + .internal_stack .truncate(n); r } @@ -1312,13 +1318,12 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { }; let ret = evaluate(env, body); { - let mut ptr = try_borrow_mut_nres(env, "internal", "for push").expect("internal for: stack is out of sync, unrecoverable"); + let mut ptr = try_borrow_mut_nres(env, "internal", "for push") + .expect("internal for: stack is out of sync, unrecoverable"); ptr.internal_stack.truncate(s); } match ret { - Err(NErr::Break(k)) => { - return Ok(k.unwrap_or(Obj::Null)) - } + Err(NErr::Break(k)) => return Ok(k.unwrap_or(Obj::Null)), Err(NErr::Continue) => (), Err(r) => return Err(r), Ok(_) => (), @@ -1471,7 +1476,7 @@ pub fn index(xr: Obj, ir: Obj) -> NRes { } } Seq::Vector(v) => Ok(Obj::Num(v[pythonic_index(v, &ii)?].clone())), - Seq::Bytes(v) => Ok(Obj::from(v[pythonic_index(v, &ii)?] as usize)), + Seq::Bytes(v) => Ok(Obj::u8(v[pythonic_index(v, &ii)?])), Seq::Stream(v) => match ii { Obj::Num(ii) => match ii.to_isize() { Some(n) => v.pythonic_index_isize(n), @@ -1589,9 +1594,7 @@ pub fn eval_lvalue_as_obj(env: &REnv, expr: &EvaluatedLvalue) -> NRes { ))) } } - EvaluatedLvalue::InternalPeek(i) => { - Env::try_borrow_peek(env, *i) - } + EvaluatedLvalue::InternalPeek(i) => Env::try_borrow_peek(env, *i), } } @@ -2244,9 +2247,7 @@ pub fn assign(env: &REnv, lhs: &EvaluatedLvalue, rt: Option<&ObjType>, rhs: Obj) "Destructuring structure failed: not type".to_string(), )), }, - EvaluatedLvalue::InternalPeek(i) => { - Env::try_borrow_set_peek(env, *i, rhs) - }, + EvaluatedLvalue::InternalPeek(i) => Env::try_borrow_set_peek(env, *i, rhs), } } @@ -2298,9 +2299,7 @@ pub fn drop_lhs(env: &REnv, lhs: &EvaluatedLvalue) -> NRes<()> { EvaluatedLvalue::Literal(_) => Ok(()), // assigning to it probably will fail later... EvaluatedLvalue::Destructure(_, vs) => drop_lhs_all(env, vs), EvaluatedLvalue::DestructureStruct(_, vs) => drop_lhs_all(env, vs), - EvaluatedLvalue::InternalPeek(i) => { - Env::try_borrow_set_peek(env, *i, Obj::Null) - }, + EvaluatedLvalue::InternalPeek(i) => Env::try_borrow_set_peek(env, *i, Obj::Null), } } @@ -2380,9 +2379,7 @@ pub fn assign_every(env: &REnv, lhs: &EvaluatedLvalue, rt: Option<&ObjType>, rhs "Can't assign-every to struct {:?}", lhs ))), - EvaluatedLvalue::InternalPeek(i) => { - Env::try_borrow_set_peek(env, *i, rhs) - } + EvaluatedLvalue::InternalPeek(i) => Env::try_borrow_set_peek(env, *i, rhs), } } diff --git a/src/lib.rs b/src/lib.rs index 7cb1875..28e5dcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,6 @@ use std::rc::Rc; use regex::Regex; use num::bigint::{BigInt, RandBigInt}; -use num::Signed; -use num::ToPrimitive; use chrono::prelude::*; @@ -56,6 +54,7 @@ pub mod few; mod gamma; mod iter; mod lex; +mod nint; pub mod nnum; mod streams; use crate::few::*; @@ -65,6 +64,7 @@ use crate::streams::*; pub use crate::core::*; pub use crate::eval::*; pub use crate::lex::Token; +use crate::nint::NInt; use crate::nnum::NNum; // can "destructure" @@ -599,7 +599,7 @@ impl Catamorphism for CataCounter { Ok(()) } fn finish(&mut self) -> NRes { - Ok(Obj::from(self.0)) + Ok(Obj::usize(self.0)) } } @@ -618,7 +618,7 @@ impl Builtin for Count { c += 1 } } - Ok(Obj::from(c)) + Ok(Obj::usize(c)) } Few2::One(a) => Ok(clone_and_part_app_last(self, a)), Few2::Two(mut a, b) => { @@ -640,7 +640,7 @@ impl Builtin for Count { } } } - Ok(Obj::from(c)) + Ok(Obj::usize(c)) } Few2::Many(x) => err_add_name(Err(NErr::argument_error_args(&x)), "count"), } @@ -706,7 +706,7 @@ impl Catamorphism for CataCountDistinct { Ok(()) } fn finish(&mut self) -> NRes { - Ok(Obj::from(self.0.len())) + Ok(Obj::usize(self.0.len())) } } @@ -716,7 +716,7 @@ struct CountDistinct; impl Builtin for CountDistinct { fn run(&self, _env: &REnv, args: Vec) -> NRes { match few(args) { - Few::One(Obj::Seq(mut s)) => Ok(Obj::from( + Few::One(Obj::Seq(mut s)) => Ok(Obj::usize( mut_seq_into_finite_iter(&mut s, "count_distinct conversion")? .map(|e| to_key(e?)) .collect::>>()? @@ -747,12 +747,12 @@ impl Builtin for TilBuiltin { match few3(args) { Few3::One(a) => Ok(clone_and_part_app_2(self, a)), Few3::Two(Obj::Num(a), Obj::Num(b)) => { - let n1 = into_bigint_ok(a)?; - let n2 = into_bigint_ok(b)?; + let n1 = into_nint_ok(a)?; + let n2 = into_nint_ok(b)?; Ok(Obj::Seq(Seq::Stream(Rc::new(Range( n1, Some(n2), - BigInt::from(1), + NInt::Small(1), ))))) } Few3::Two(Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { @@ -771,9 +771,9 @@ impl Builtin for TilBuiltin { } } Few3::Three(Obj::Num(a), Obj::Num(b), Obj::Num(c)) => { - let n1 = into_bigint_ok(a)?; - let n2 = into_bigint_ok(b)?; - let n3 = into_bigint_ok(c)?; + let n1 = into_nint_ok(a)?; + let n2 = into_nint_ok(b)?; + let n3 = into_nint_ok(c)?; Ok(Obj::Seq(Seq::Stream(Rc::new(Range(n1, Some(n2), n3))))) } c => err_add_name(Err(NErr::argument_error_few3(&c)), "til"), @@ -804,15 +804,15 @@ impl Builtin for ToBuiltin { Few3::One(a) => Ok(clone_and_part_app_2(self, a)), Few3::Two(a, b) => self.run2(_env, a, b), Few3::Three(Obj::Num(a), Obj::Num(b), Obj::Num(c)) => { - let n1 = into_bigint_ok(a)?; - let n2 = into_bigint_ok(b)?; - let n3 = into_bigint_ok(c)?; + let n1 = into_nint_ok(a)?; + let n2 = into_nint_ok(b)?; + let n3 = into_nint_ok(c)?; Ok(Obj::Seq(Seq::Stream(Rc::new(Range( n1, Some(if n3.is_negative() { - n2 - 1usize + n2 - NInt::Small(1) } else { - n2 + 1usize + n2 + NInt::Small(1) }), n3, ))))) @@ -823,12 +823,12 @@ impl Builtin for ToBuiltin { fn run2(&self, _env: &REnv, a: Obj, b: Obj) -> NRes { match (a, b) { (Obj::Num(a), Obj::Num(b)) => { - let n1 = into_bigint_ok(a)?; - let n2 = into_bigint_ok(b)?; + let n1 = into_nint_ok(a)?; + let n2 = into_nint_ok(b)?; Ok(Obj::Seq(Seq::Stream(Rc::new(Range( n1, - Some(n2 + 1usize), - BigInt::from(1), + Some(n2 + NInt::Small(1)), + NInt::Small(1), ))))) } (Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { @@ -1838,9 +1838,7 @@ impl Builtin for TwoNumsToNumsBuiltin { } fn run1(&self, _env: &REnv, arg: Obj) -> NRes { match arg { - arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_))) => { - Ok(clone_and_part_app_2(self, arg)) - } + arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_))) => Ok(clone_and_part_app_2(self, arg)), a => Err(NErr::argument_error(format!( "{} only accepts numbers (or vectors), got {}", self.name, a @@ -1913,9 +1911,7 @@ impl Builtin for TwoNumsBuiltin { } fn run1(&self, _env: &REnv, arg: Obj) -> NRes { match arg { - arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_))) => { - Ok(clone_and_part_app_2(self, arg)) - } + arg @ (Obj::Num(_) | Obj::Seq(Seq::Vector(_))) => Ok(clone_and_part_app_2(self, arg)), a => Err(NErr::argument_error(format!( "{} only accepts numbers (or vectors), got {}", self.name, a @@ -1957,7 +1953,7 @@ fn linear_index_isize(xr: Seq, i: isize) -> NRes { match xr { Seq::List(xx) => Ok(xx[pythonic_index_isize(&xx, i)?].clone()), Seq::Vector(x) => Ok(Obj::Num(x[pythonic_index_isize(&x, i)?].clone())), - Seq::Bytes(x) => Ok(Obj::from(x[pythonic_index_isize(&x, i)?] as usize)), + Seq::Bytes(x) => Ok(Obj::u8(x[pythonic_index_isize(&x, i)?])), Seq::String(s) => { let bs = s.as_bytes(); let i = pythonic_index_isize(bs, i)?; @@ -1985,7 +1981,7 @@ fn obj_cyclic_index(xr: Obj, ir: Obj) -> NRes { FmtObj::debug(&ii) ))), Seq::Vector(xx) => Ok(Obj::Num(xx[cyclic_index(xx, &ii)?].clone())), - Seq::Bytes(xx) => Ok(Obj::from(xx[cyclic_index(xx, &ii)?] as usize)), + Seq::Bytes(xx) => Ok(Obj::u8(xx[cyclic_index(xx, &ii)?])), Seq::Stream(v) => Err(NErr::type_error(format!( "Can't cyclically index stream {:?}[{}]", v, @@ -2048,7 +2044,7 @@ fn safe_index(xr: Obj, ir: Obj) -> NRes { None => Ok(Obj::Null), }, Seq::Bytes(v) => match safe_index_inner(v, ii) { - Some(i) => Ok(Obj::from(v[i] as usize)), + Some(i) => Ok(Obj::u8(v[i])), None => Ok(Obj::Null), }, Seq::Stream(v) => Err(NErr::type_error(format!( @@ -2503,7 +2499,7 @@ fn uncons(s: Seq) -> NRes> { Ok(None) } else { let head = Rc::make_mut(&mut s).remove(0); - Ok(Some((Obj::from(head), Seq::Bytes(s)))) + Ok(Some((Obj::u8(head), Seq::Bytes(s)))) } } Seq::Stream(s) => { @@ -2532,7 +2528,7 @@ fn unsnoc(s: Seq) -> NRes> { }, Seq::Bytes(mut s) => match Rc::make_mut(&mut s).pop() { None => Ok(None), - Some(e) => Ok(Some((Seq::Bytes(s), Obj::from(e)))), + Some(e) => Ok(Some((Seq::Bytes(s), Obj::u8(e)))), }, Seq::Stream(s) => unsnoc(Seq::List(Rc::new(s.force()?))), } @@ -2610,10 +2606,7 @@ fn multi_group_by_eq(v: Seq) -> NRes> { multimulti!(v, grouped_by(v, |a, b| Ok(a == b))) } fn multi_group_by(env: &REnv, f: Func, v: Seq) -> NRes> { - multimulti!( - v, - grouped_by(v, |a, b| Ok(f.run2(env, a, b)?.truthy())) - ) + multimulti!(v, grouped_by(v, |a, b| Ok(f.run2(env, a, b)?.truthy()))) } fn multi_group_all_with(env: &REnv, f: Func, v: Seq) -> NRes> { multimulti!(v, grouped_all_with(v, |x| f.run1(env, x))) @@ -3073,11 +3066,7 @@ pub fn initialize(env: &mut Env) { env.insert_builtin(OneNumBuiltin { name: "is_prime".to_string(), - body: |a| { - Ok(Obj::Num(NNum::iverson(nnum::lazy_is_prime( - &into_bigint_ok(a)?, - )))) - }, + body: |a| Ok(Obj::Num(NNum::iverson(into_nint_ok(a)?.lazy_is_prime()))), }); env.insert_builtin(OneNumBuiltin { name: "factorize".to_string(), @@ -3085,7 +3074,7 @@ pub fn initialize(env: &mut Env) { Ok(Obj::list( nnum::lazy_factorize(into_bigint_ok(a)?) .into_iter() - .map(|(a, e)| Obj::list(vec![Obj::from(a), Obj::from(e)])) + .map(|(a, e)| Obj::list(vec![Obj::from(a), Obj::usize(e)])) .collect(), )) }, @@ -3098,7 +3087,7 @@ pub fn initialize(env: &mut Env) { Obj::Seq(Seq::String(s)) => { let mut c = s.chars(); match (c.next(), c.next()) { - (Some(ch), None) => Ok(Obj::from(ch as usize)), + (Some(ch), None) => Ok(Obj::usize(ch as usize)), (None, _) => Err(NErr::value_error("ord of empty string".to_string())), (_, Some(_)) => { Err(NErr::value_error("ord of string with len > 1".to_string())) @@ -3123,7 +3112,7 @@ pub fn initialize(env: &mut Env) { name: "len".to_string(), body: |arg| match arg { Obj::Seq(s) => match s.len() { - Some(n) => Ok(Obj::from(n)), + Some(n) => Ok(Obj::usize(n)), None => Ok(Obj::from(f64::INFINITY)), }, e => Err(NErr::type_error(format!( @@ -3263,7 +3252,7 @@ pub fn initialize(env: &mut Env) { Obj::Num(NNum::Int(x)) => Ok(Obj::Seq(Seq::Stream(Rc::new(Range( x, None, - BigInt::from(1), + NInt::Small(1), ))))), e => Err(NErr::argument_error_1(&e)), }, @@ -3343,8 +3332,7 @@ pub fn initialize(env: &mut Env) { let it = mut_obj_into_iter(&mut a, "map")?; match b { Obj::Func(b, _) => Ok(Obj::list( - it.map(|e| b.run1(env, e?)) - .collect::>>()?, + it.map(|e| b.run1(env, e?)).collect::>>()?, )), _ => Err(NErr::type_error("not callable".to_string())), } @@ -3595,7 +3583,7 @@ pub fn initialize(env: &mut Env) { *c.entry(to_key(e?)?).or_insert(0) += 1; } Ok(Obj::Seq(Seq::Dict( - Rc::new(c.into_iter().map(|(k, v)| (k, Obj::from(v))).collect()), + Rc::new(c.into_iter().map(|(k, v)| (k, Obj::usize(v))).collect()), Some(Box::new(Obj::zero())), ))) }, @@ -4254,7 +4242,7 @@ pub fn initialize(env: &mut Env) { let mut it = mut_obj_into_iter(&mut a, "locate")?.enumerate(); while let Some((i, x)) = it.next() { if f.run1(env, x?.clone())?.truthy() { - return Ok(Obj::from(i)); + return Ok(Obj::usize(i)); } } Err(NErr::value_error("didn't find".to_string())) @@ -4262,7 +4250,7 @@ pub fn initialize(env: &mut Env) { (Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { match a.find(&*b) { // this is the byte index! shrug - Some(i) => Ok(Obj::from(i)), + Some(i) => Ok(Obj::usize(i)), None => Err(NErr::value_error("didn't find".to_string())), } } @@ -4270,7 +4258,7 @@ pub fn initialize(env: &mut Env) { let mut it = mut_obj_into_iter(&mut a, "locate")?.enumerate(); while let Some((i, x)) = it.next() { if x? == b { - return Ok(Obj::from(i)); + return Ok(Obj::usize(i)); } } Err(NErr::value_error("didn't find".to_string())) @@ -4284,7 +4272,7 @@ pub fn initialize(env: &mut Env) { let mut it = mut_obj_into_iter(&mut a, "locate?")?.enumerate(); while let Some((i, x)) = it.next() { if f.run1(env, x?.clone())?.truthy() { - return Ok(Obj::from(i)); + return Ok(Obj::usize(i)); } } Ok(Obj::Null) @@ -4292,7 +4280,7 @@ pub fn initialize(env: &mut Env) { (Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { match a.find(&*b) { // this is the byte index! shrug - Some(i) => Ok(Obj::from(i)), + Some(i) => Ok(Obj::usize(i)), None => Ok(Obj::Null), } } @@ -4300,7 +4288,7 @@ pub fn initialize(env: &mut Env) { let mut it = mut_obj_into_iter(&mut a, "locate?")?.enumerate(); while let Some((i, x)) = it.next() { if x? == b { - return Ok(Obj::from(i)); + return Ok(Obj::usize(i)); } } Ok(Obj::Null) @@ -4432,7 +4420,7 @@ pub fn initialize(env: &mut Env) { Ok(Obj::list( mut_obj_into_iter(&mut a, "enumerate conversion")? .enumerate() - .map(|(k, v)| Ok(Obj::list(vec![Obj::from(k), v?]))) + .map(|(k, v)| Ok(Obj::list(vec![Obj::usize(k), v?]))) .collect::>>()?, )) }, @@ -4616,11 +4604,8 @@ pub fn initialize(env: &mut Env) { let mut ret = Vec::new(); while (&a).is_positive() { ret.push( - char::from_digit( - ((&a) % base).to_u32().expect("str_radix bad"), - base, - ) - .expect("str_radix bad"), + char::from_digit((&a % &r).to_u32().expect("str_radix bad"), base) + .expect("str_radix bad"), ); a /= base; } @@ -4643,7 +4628,7 @@ pub fn initialize(env: &mut Env) { name: "int_radix".to_string(), body: |a, r| { if let Obj::Num(n) = r { - if let Some(base) = n.to_bigint().and_then(BigInt::to_u32) { + if let Some(base) = n.to_nint().and_then(NInt::to_u32) { if 2 <= base && base <= 36 { let mut x = BigInt::from(0); match a { @@ -5134,9 +5119,9 @@ pub fn initialize(env: &mut Env) { env.insert_builtin(TwoArgBuiltin { name: "random_range".to_string(), body: |a, b| match (a, b) { - (Obj::Num(NNum::Int(a)), Obj::Num(NNum::Int(b))) => { - Ok(Obj::from(rand::thread_rng().gen_bigint_range(&a, &b))) - } + (Obj::Num(NNum::Int(a)), Obj::Num(NNum::Int(b))) => Ok(Obj::from( + rand::thread_rng().gen_bigint_range(&a.to_bigint(), &b.to_bigint()), + )), (a, b) => Err(NErr::argument_error_2(&a, &b)), }, }); @@ -5318,6 +5303,14 @@ pub fn initialize(env: &mut Env) { ))) }, }); + + env.insert_builtin(OneArgBuiltin { + name: "is_big".to_string(), + body: |a| match a { + Obj::Num(NNum::Int(NInt::Big(_))) => Ok(Obj::one()), + _ => Ok(Obj::zero()), + }, + }); } // copypasta diff --git a/src/nint.rs b/src/nint.rs new file mode 100644 index 0000000..6b96754 --- /dev/null +++ b/src/nint.rs @@ -0,0 +1,452 @@ +use num::bigint::{BigInt, Sign}; +use num::pow::Pow; +use num::BigUint; +use num::Integer; +use num::{Signed, ToPrimitive, Zero}; +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::mem; +use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub}; +use std::ops::{AddAssign, DivAssign, MulAssign}; + +use std::borrow::Cow; + +#[derive(Debug, Clone)] +pub enum NInt { + Small(i64), + Big(BigInt), +} + +macro_rules! forward { + ($method:ident, $ty:ty) => { + pub fn $method(&self) -> $ty { + match self { + NInt::Small(n) => n.$method(), + NInt::Big(n) => n.$method(), + } + } + }; +} +impl From for NInt { + fn from(x: BigInt) -> Self { + match x.to_i64() { + Some(x) => NInt::Small(x), + None => NInt::Big(x), + } + } +} +impl NInt { + pub fn usize(x: usize) -> Self { + match x.to_i64() { + Some(x) => NInt::Small(x), + None => NInt::Big(BigInt::from(x)), + } + } +} +impl<'a> NInt { + pub fn to_bigint(&'a self) -> Cow<'a, BigInt> { + match self { + NInt::Small(n) => Cow::Owned(BigInt::from(*n)), + NInt::Big(n) => Cow::Borrowed(n), + } + } + forward!(to_f64, Option); + forward!(to_i32, Option); + forward!(to_i64, Option); + forward!(to_isize, Option); + forward!(to_u32, Option); + forward!(to_u8, Option); + forward!(to_usize, Option); + forward!(is_zero, bool); + forward!(is_positive, bool); + forward!(is_negative, bool); +} +impl NInt { + pub fn into_bigint(self) -> BigInt { + match self { + NInt::Small(n) => BigInt::from(n), + NInt::Big(n) => n, + } + } +} + +macro_rules! forward_display { + ($impl:ident) => { + impl fmt::$impl for NInt { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + NInt::Small(n) => fmt::$impl::fmt(n, formatter), + NInt::Big(n) => fmt::$impl::fmt(n, formatter), + } + } + } + }; +} +forward_display!(Display); +forward_display!(LowerHex); +forward_display!(UpperHex); +forward_display!(Binary); +forward_display!(Octal); + +macro_rules! impl_binary { + ($imp:ident, $method:ident, $func:expr) => { + impl $imp for NInt { + type Output = NInt; + fn $method(self, other: NInt) -> NInt { + match (&self, &other) { + (NInt::Small(a), NInt::Small(b)) => NInt::Small($func(*a, *b)), + _ => NInt::Big($func(self.into_bigint(), other.into_bigint())), + } + } + } + impl $imp<&NInt> for NInt { + type Output = NInt; + fn $method(self, other: &NInt) -> NInt { + match (&self, other) { + (NInt::Small(a), NInt::Small(b)) => NInt::Small($func(*a, *b)), + _ => NInt::Big(match other { + NInt::Big(b) => $func(self.into_bigint(), b), + NInt::Small(b) => $func(self.into_bigint(), BigInt::from(*b)), + }), + } + } + } + impl $imp for &NInt { + type Output = NInt; + fn $method(self, other: NInt) -> NInt { + match (self, &other) { + (NInt::Small(a), NInt::Small(b)) => NInt::Small($func(*a, *b)), + _ => NInt::Big(match self { + NInt::Big(a) => $func(a, other.into_bigint()), + NInt::Small(a) => $func(BigInt::from(*a), other.into_bigint()), + }), + } + } + } + impl $imp<&NInt> for &NInt { + type Output = NInt; + fn $method(self, other: &NInt) -> NInt { + match (self, other) { + (NInt::Small(a), NInt::Small(b)) => NInt::Small($func(*a, *b)), + (NInt::Big(a), NInt::Small(b)) => NInt::Big($func(a, BigInt::from(*b))), + (NInt::Small(a), NInt::Big(b)) => NInt::Big($func(BigInt::from(*a), b)), + (NInt::Big(a), NInt::Big(b)) => NInt::Big($func(a, b)), + } + } + } + }; +} + +impl_binary!(BitAnd, bitand, BitAnd::bitand); +impl_binary!(BitOr, bitor, BitOr::bitor); +impl_binary!(BitXor, bitxor, BitXor::bitxor); + +macro_rules! impl_binary_checked { + ($imp:ident, $method:ident, $func:expr, $checked:ident) => { + impl $imp for NInt { + type Output = NInt; + fn $method(self, other: NInt) -> NInt { + match (&self, &other) { + (NInt::Small(a), NInt::Small(b)) => { + if let Some(r) = i64::$checked(*a, *b) { + return NInt::Small(r); + } + } + _ => (), + } + NInt::Big($func(self.into_bigint(), other.into_bigint())) + } + } + impl $imp<&NInt> for NInt { + type Output = NInt; + fn $method(self, other: &NInt) -> NInt { + match (&self, other) { + (NInt::Small(a), NInt::Small(b)) => { + if let Some(r) = i64::$checked(*a, *b) { + return NInt::Small(r); + } + } + _ => (), + } + NInt::Big(match other { + NInt::Big(b) => $func(self.into_bigint(), b), + NInt::Small(b) => $func(self.into_bigint(), BigInt::from(*b)), + }) + } + } + impl $imp for &NInt { + type Output = NInt; + fn $method(self, other: NInt) -> NInt { + match (self, &other) { + (NInt::Small(a), NInt::Small(b)) => { + if let Some(r) = i64::$checked(*a, *b) { + return NInt::Small(r); + } + } + _ => (), + } + NInt::Big(match self { + NInt::Big(a) => $func(a, other.into_bigint()), + NInt::Small(a) => $func(BigInt::from(*a), other.into_bigint()), + }) + } + } + impl $imp<&NInt> for &NInt { + type Output = NInt; + fn $method(self, other: &NInt) -> NInt { + match (self, other) { + (NInt::Small(a), NInt::Small(b)) => { + if let Some(r) = i64::$checked(*a, *b) { + return NInt::Small(r); + } + } + _ => (), + } + NInt::Big(match (self, other) { + (NInt::Small(a), NInt::Small(b)) => $func(BigInt::from(*a), BigInt::from(*b)), + (NInt::Big(a), NInt::Small(b)) => $func(a, BigInt::from(*b)), + (NInt::Small(a), NInt::Big(b)) => $func(BigInt::from(*a), b), + (NInt::Big(a), NInt::Big(b)) => $func(a, b), + }) + } + } + }; +} + +impl_binary_checked!(Add, add, Add::add, checked_add); +impl_binary_checked!(Sub, sub, Sub::sub, checked_sub); +impl_binary_checked!(Mul, mul, Mul::mul, checked_mul); +impl_binary_checked!(Div, div, Div::div, checked_div); +impl_binary_checked!(Rem, rem, Rem::rem, checked_rem); + +impl Neg for NInt { + type Output = NInt; + + fn neg(self) -> NInt { + NInt::from(-self.into_bigint()) + } +} +impl Neg for &NInt { + type Output = NInt; + + fn neg(self) -> NInt { + NInt::from(-self.to_bigint().into_owned()) + } +} +impl Not for NInt { + type Output = NInt; + + fn not(self) -> NInt { + match self { + NInt::Small(a) => NInt::Small(!a), + NInt::Big(a) => NInt::Big(!a), + } + } +} +impl Not for &NInt { + type Output = NInt; + + fn not(self) -> NInt { + match self { + NInt::Small(a) => NInt::Small(!a), + NInt::Big(a) => NInt::Big(!a), + } + } +} +impl PartialEq for NInt { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (NInt::Small(a), NInt::Small(b)) => a == b, + (NInt::Small(a), NInt::Big(b)) => b.to_i64().map_or(false, |n| *a == n), + (NInt::Big(a), NInt::Small(b)) => a.to_i64().map_or(false, |n| n == *b), + (NInt::Big(a), NInt::Big(b)) => a == b, + } + } +} +impl Eq for NInt {} +impl PartialOrd for NInt { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (NInt::Small(a), NInt::Small(b)) => a.partial_cmp(b), + _ => (&*self.to_bigint()).partial_cmp(&*other.to_bigint()), + } + } +} +impl Ord for NInt { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (NInt::Small(a), NInt::Small(b)) => a.cmp(b), + _ => (&*self.to_bigint()).cmp(&*other.to_bigint()), + } + } +} +impl Hash for NInt { + fn hash(&self, state: &mut H) { + match self { + NInt::Small(a) => state.write_i64(*a), + NInt::Big(a) => match a.to_i64() { + Some(a) => state.write_i64(a), + None => a.hash(state), + }, + } + } +} + +impl<'a> NInt { + pub fn magnitude(&'a self) -> Cow<'a, BigUint> { + match self.to_bigint() { + Cow::Owned(a) => Cow::Owned(a.into_parts().1), + Cow::Borrowed(a) => Cow::Borrowed(a.magnitude()), + } + } +} +impl NInt { + pub fn div_floor(&self, other: &NInt) -> NInt { + NInt::Big(self.to_bigint().div_floor(&*other.to_bigint())) + } + pub fn mod_floor(&self, other: &NInt) -> NInt { + NInt::Big(self.to_bigint().mod_floor(&*other.to_bigint())) + } + pub fn abs(&self) -> NInt { + match self { + NInt::Small(x) => match x.checked_abs() { + Some(x) => return NInt::Small(x), + None => (), + }, + NInt::Big(_) => (), + } + NInt::Big(self.to_bigint().abs()) + } + pub fn sign(&self) -> Sign { + match self { + NInt::Small(a) => { + if *a == 0 { + Sign::NoSign + } else if *a > 0 { + Sign::Plus + } else { + Sign::Minus + } + } + NInt::Big(a) => a.sign(), + } + } + pub fn pow(&self, other: u32) -> Self { + NInt::Big((&*self.to_bigint()).pow(other)) + } + pub fn pow_maybe_recip(&self, other: &Self) -> (bool, Self) { + match other.sign() { + Sign::NoSign => (false, NInt::Small(1)), + Sign::Plus => ( + false, + NInt::Big(Pow::pow(&*self.to_bigint(), &*other.magnitude())), + ), + Sign::Minus => ( + true, + NInt::Big(Pow::pow(&*self.to_bigint(), &*other.magnitude())), + ), + } + } + pub fn signum(&self) -> NInt { + match self { + NInt::Small(a) => NInt::Small(a.signum()), + NInt::Big(a) => match a.sign() { + Sign::Minus => NInt::Small(1), + Sign::NoSign => NInt::Small(0), + Sign::Plus => NInt::Small(-1), + }, + } + } + pub fn gcd(&self, other: &Self) -> NInt { + NInt::Big(Integer::gcd(&*self.to_bigint(), &*other.to_bigint())) + } + pub fn sqrt(&self) -> NInt { + NInt::Big(self.to_bigint().sqrt()) + } + pub fn lte(&self, other: i64) -> bool { + match self { + NInt::Small(a) => *a <= other, + NInt::Big(a) => a <= &BigInt::from(other), + } + } + pub fn lazy_is_prime(self: &Self) -> bool { + if self.lte(1) { + false + } else if self.lte(3) { + true + } else if (self % NInt::Small(2)).is_zero() || (self % NInt::Small(3)).is_zero() { + false + } else { + let s = self.sqrt(); // truncates + let mut f = NInt::Small(5); + loop { + if f > s { + return true; + } + if (self % &f).is_zero() { + return false; + } + + f += 2; + if f > s { + return true; + } + if (self % &f).is_zero() { + return false; + } + + f += 4; + } + } + } + pub fn factorial(&self) -> NInt { + let mut ret = NInt::Small(1); + let mut i = NInt::Small(1); + while &i < self { + ret = ret * &i; + i += 1; + } + ret + } +} + +impl Shl for NInt { + type Output = Self; + fn shl(self, other: usize) -> Self { + NInt::Big(self.into_bigint() << other) + } +} +impl Shr for NInt { + type Output = Self; + fn shr(self, other: usize) -> Self { + NInt::Big(self.into_bigint() >> other) + } +} + +impl AddAssign<&NInt> for NInt { + fn add_assign(&mut self, other: &NInt) { + *self = mem::replace(self, NInt::Small(0)) + &*other; + } +} + +impl MulAssign<&NInt> for NInt { + fn mul_assign(&mut self, other: &NInt) { + *self = mem::replace(self, NInt::Small(0)) * &*other; + } +} + +impl AddAssign for NInt { + fn add_assign(&mut self, other: i64) { + *self += &NInt::Small(other) + } +} + +impl DivAssign for NInt { + fn div_assign(&mut self, other: u32) { + match self { + NInt::Small(a) => *a /= other as i64, + NInt::Big(a) => *a /= other, + } + } +} diff --git a/src/nnum.rs b/src/nnum.rs index e75858d..065eadd 100644 --- a/src/nnum.rs +++ b/src/nnum.rs @@ -5,7 +5,7 @@ use num::bigint::ToBigInt; use num::bigint::{BigInt, Sign}; use num::complex::Complex64; use num::pow::Pow; -use num::{BigRational, Integer}; +use num::BigRational; use num::{One, Signed, ToPrimitive, Zero}; use std::cmp::Ordering; use std::fmt; @@ -16,11 +16,12 @@ use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Shl, Shr, Su use std::ops::{AddAssign, SubAssign}; use crate::gamma; +use crate::nint::NInt; // wow, BigRationals are huge #[derive(Debug, Clone)] pub enum NNum { - Int(BigInt), + Int(NInt), Rational(Box), Float(f64), Complex(Complex64), @@ -54,9 +55,14 @@ forward_display!(Octal, Octal, Display); forward_display!(LowerExp, Display, LowerExp); forward_display!(UpperExp, Display, UpperExp); +impl From for NNum { + fn from(x: NInt) -> Self { + NNum::Int(x) + } +} impl From for NNum { fn from(x: BigInt) -> Self { - NNum::Int(x) + NNum::Int(NInt::from(x)) } } impl From for NNum { @@ -64,19 +70,14 @@ impl From for NNum { NNum::Rational(Box::new(x)) } } -/* -impl From for NNum { - fn from(x: i32) -> Self { NNum::Int(BigInt::from(x)) } -} -*/ impl From for NNum { fn from(x: f64) -> Self { NNum::Float(x) } } -impl From for NNum { - fn from(x: usize) -> Self { - NNum::Int(BigInt::from(x)) +impl From for NNum { + fn from(x: i64) -> Self { + NNum::Int(NInt::Small(x)) } } impl From for NNum { @@ -84,14 +85,22 @@ impl From for NNum { NNum::Complex(z) } } +impl NNum { + pub fn usize(x: usize) -> Self { + NNum::Int(NInt::usize(x)) + } + pub fn u8(x: u8) -> Self { + NNum::Int(NInt::Small(x as i64)) + } +} trait PowIF: Sized { fn powi(self, n: i32) -> Self; fn powf(self, f: f64) -> Self; - fn powif(self, b: &BigInt) -> Self { + fn powif(self, b: &NInt) -> Self { match b.to_i32() { Some(ib) => self.powi(ib), - None => self.powf(bigint_to_f64_or_inf(b)), + None => self.powf(nint_to_f64_or_inf(b)), } } } @@ -123,7 +132,7 @@ fn powf_pdnum(a: f64, b: f64) -> NNum { NNum::from(fx) } -fn powif_pdnum(a: f64, b: &BigInt) -> NNum { +fn powif_pdnum(a: f64, b: &NInt) -> NNum { let fx = a.powif(b); if fx.is_nan() { let zx = Complex64::from(a).powif(b); @@ -134,25 +143,14 @@ fn powif_pdnum(a: f64, b: &BigInt) -> NNum { NNum::from(fx) } -fn pow_big_ints(a: &BigInt, b: &BigInt) -> NNum { - match b.sign() { - num::bigint::Sign::NoSign => NNum::from(1), - num::bigint::Sign::Plus => NNum::from(Pow::pow(a, b.magnitude())), - num::bigint::Sign::Minus => { - NNum::from(BigRational::from(Pow::pow(a, b.magnitude())).recip()) - } +fn pow_big_ints(a: &NInt, b: &NInt) -> NNum { + match a.pow_maybe_recip(b) { + (false, r) => NNum::Int(r), + (true, r) => NNum::from(BigRational::from(r.into_bigint()).recip()), } } -fn factorial_big_int(a: &BigInt) -> BigInt { - let mut ret = BigInt::one(); - for i in num::range_inclusive(BigInt::one(), BigInt::clone(a)) { - ret *= i; - } - ret -} - -fn bigint_to_f64_or_inf(i: &BigInt) -> f64 { +fn nint_to_f64_or_inf(i: &NInt) -> f64 { i.to_f64().unwrap_or_else(|| { if i.is_positive() { f64::INFINITY @@ -177,8 +175,8 @@ macro_rules! forward_int_coercion { pub fn $method(&self) -> Option { match self { NNum::Int(_) => Some(self.clone()), - NNum::Rational(r) => Some(NNum::Int(r.$method().to_integer())), - NNum::Float(f) => Some(f.$method().to_bigint().map_or(self.clone(), NNum::Int)), + NNum::Rational(r) => Some(NNum::from(r.$method().to_integer())), + NNum::Float(f) => Some(f.$method().to_bigint().map_or(self.clone(), NNum::from)), NNum::Complex(_) => None, } } @@ -225,7 +223,7 @@ impl NNum { pub fn signum(&self) -> NNum { match self { NNum::Int(k) => NNum::Int(k.signum()), - NNum::Rational(k) => NNum::Int(k.signum().to_integer()), + NNum::Rational(k) => NNum::from(k.signum().to_integer()), NNum::Float(f) => { // This is NOT Rust's f64's signum. We want +/-0 to give 0 (for consistency with // integers) @@ -260,7 +258,7 @@ impl NNum { pub fn to_rational(&self) -> Option { match self { - NNum::Int(i) => Some(BigRational::from(i.clone())), + NNum::Int(i) => Some(BigRational::from(i.to_bigint().into_owned())), NNum::Rational(r) => Some(*r.clone()), NNum::Float(_) => None, NNum::Complex(_) => None, @@ -278,7 +276,7 @@ impl NNum { pub fn to_f64_or_inf_or_complex(&self) -> Result { match self { - NNum::Int(i) => Ok(bigint_to_f64_or_inf(i)), + NNum::Int(i) => Ok(nint_to_f64_or_inf(i)), NNum::Rational(r) => Ok(rational_to_f64_or_inf(r)), NNum::Float(f) => Ok(*f), NNum::Complex(z) => Err(*z), @@ -326,7 +324,7 @@ impl NNum { pub fn imaginary_part(&self) -> NNum { match self { - NNum::Int(_) => NNum::Int(BigInt::from(0)), + NNum::Int(_) => NNum::Int(NInt::Small(0)), NNum::Rational(_) => NNum::Rational(Box::new(BigRational::from(BigInt::from(0)))), NNum::Float(_) => NNum::Float(0.0), NNum::Complex(z) => NNum::Float(z.im), @@ -350,11 +348,11 @@ impl NNum { match (self, other) { (NNum::Int(a), NNum::Int(b)) => pow_big_ints(a, b), (NNum::Int(a), NNum::Rational(b)) => { - powf_pdnum(bigint_to_f64_or_inf(a), rational_to_f64_or_inf(b)) + powf_pdnum(nint_to_f64_or_inf(a), rational_to_f64_or_inf(b)) } - (NNum::Int(a), NNum::Float(b)) => powf_pdnum(bigint_to_f64_or_inf(a), *b), + (NNum::Int(a), NNum::Float(b)) => powf_pdnum(nint_to_f64_or_inf(a), *b), - (NNum::Rational(a), NNum::Int(b)) => NNum::from(Pow::pow(&**a, b)), + (NNum::Rational(a), NNum::Int(b)) => NNum::from(Pow::pow(&**a, &*b.to_bigint())), (NNum::Rational(a), NNum::Rational(b)) => { powf_pdnum(rational_to_f64_or_inf(a), rational_to_f64_or_inf(b)) } @@ -375,7 +373,7 @@ impl NNum { pub fn factorial(&self) -> NNum { match self { - NNum::Int(a) => NNum::Int(factorial_big_int(a)), + NNum::Int(a) => NNum::Int(a.factorial()), NNum::Rational(r) => NNum::Float(gamma::gamma(rational_to_f64_or_inf(r) + 1.0)), NNum::Float(f) => NNum::Float(gamma::gamma(f + 1.0)), NNum::Complex(_) => { @@ -392,20 +390,27 @@ impl NNum { } } - pub fn to_bigint(&self) -> Option<&BigInt> { + pub fn to_nint(&self) -> Option<&NInt> { match self { NNum::Int(n) => Some(n), _ => None, } } - pub fn into_bigint(self) -> Option { + pub fn into_nint(self) -> Option { match self { NNum::Int(n) => Some(n), _ => None, } } + pub fn into_bigint(self) -> Option { + match self { + NNum::Int(n) => Some(n.into_bigint()), + _ => None, + } + } + pub fn to_isize(&self) -> Option { match self { NNum::Int(n) => n.to_isize(), @@ -444,8 +449,8 @@ impl NNum { } // this seems... nontrivial?? -fn cmp_bigint_f64(a: &BigInt, b: &f64) -> Option { - if let Some(bi) = to_bigint_if_int(*b) { +fn cmp_nint_f64(a: &NInt, b: &f64) -> Option { + if let Some(bi) = to_nint_if_int(*b) { Some(a.cmp(&bi)) } else if b.is_infinite() { if b.is_sign_positive() { @@ -454,7 +459,7 @@ fn cmp_bigint_f64(a: &BigInt, b: &f64) -> Option { Some(Ordering::Greater) } } else { - b.floor().to_bigint().map(|bi| match a.cmp(&bi) { + b.floor().to_bigint().map(|bi| match a.cmp(&NInt::Big(bi)) { Ordering::Less => Ordering::Less, Ordering::Equal => Ordering::Less, Ordering::Greater => Ordering::Greater, @@ -464,13 +469,13 @@ fn cmp_bigint_f64(a: &BigInt, b: &f64) -> Option { // useful to project down to this for ease of doing stuff enum NNumReal<'a> { - Int(&'a BigInt), + Int(&'a NInt), Float(f64), } -fn to_bigint_if_int(f: f64) -> Option { +fn to_nint_if_int(f: f64) -> Option { if f == f.trunc() { - f.to_bigint() + f.to_bigint().map(NInt::Big) } else { None } @@ -481,10 +486,10 @@ impl<'a> PartialEq for NNumReal<'a> { match (self, other) { (NNumReal::Int(a), NNumReal::Int(b)) => a == b, (NNumReal::Int(a), NNumReal::Float(b)) => { - to_bigint_if_int(*b).map_or(false, |x| &x == *a) + to_nint_if_int(*b).map_or(false, |x| &x == *a) } (NNumReal::Float(a), NNumReal::Int(b)) => { - to_bigint_if_int(*a).map_or(false, |x| &x == *b) + to_nint_if_int(*a).map_or(false, |x| &x == *b) } (NNumReal::Float(a), NNumReal::Float(b)) => a == b, } @@ -495,8 +500,8 @@ impl<'a> PartialOrd for NNumReal<'a> { fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { (NNumReal::Int(a), NNumReal::Int(b)) => Some(a.cmp(b)), - (NNumReal::Int(a), NNumReal::Float(b)) => cmp_bigint_f64(a, b), - (NNumReal::Float(a), NNumReal::Int(b)) => cmp_bigint_f64(b, a).map(|ord| ord.reverse()), + (NNumReal::Int(a), NNumReal::Float(b)) => cmp_nint_f64(a, b), + (NNumReal::Float(a), NNumReal::Int(b)) => cmp_nint_f64(b, a).map(|ord| ord.reverse()), (NNumReal::Float(a), NNumReal::Float(b)) => a.partial_cmp(b), } } @@ -507,10 +512,10 @@ impl<'a> NNumReal<'a> { match (self, other) { (NNumReal::Int(a), NNumReal::Int(b)) => a.cmp(b), (NNumReal::Int(a), NNumReal::Float(b)) => { - cmp_bigint_f64(a, b).unwrap_or(Ordering::Greater) + cmp_nint_f64(a, b).unwrap_or(Ordering::Greater) } (NNumReal::Float(a), NNumReal::Int(b)) => { - cmp_bigint_f64(b, a).map_or(Ordering::Less, |ord| ord.reverse()) + cmp_nint_f64(b, a).map_or(Ordering::Less, |ord| ord.reverse()) } (NNumReal::Float(a), NNumReal::Float(b)) => { a.partial_cmp(b).unwrap_or(b.is_nan().cmp(&a.is_nan())) @@ -521,11 +526,9 @@ impl<'a> NNumReal<'a> { fn total_cmp_big_nan(&self, other: &Self) -> Ordering { match (self, other) { (NNumReal::Int(a), NNumReal::Int(b)) => a.cmp(b), - (NNumReal::Int(a), NNumReal::Float(b)) => { - cmp_bigint_f64(a, b).unwrap_or(Ordering::Less) - } + (NNumReal::Int(a), NNumReal::Float(b)) => cmp_nint_f64(a, b).unwrap_or(Ordering::Less), (NNumReal::Float(a), NNumReal::Int(b)) => { - cmp_bigint_f64(b, a).map_or(Ordering::Greater, |ord| ord.reverse()) + cmp_nint_f64(b, a).map_or(Ordering::Greater, |ord| ord.reverse()) } (NNumReal::Float(a), NNumReal::Float(b)) => { a.partial_cmp(b).unwrap_or(a.is_nan().cmp(&b.is_nan())) @@ -582,8 +585,8 @@ impl NNum { } fn consistent_hash_f64(f: f64, state: &mut H) { - match to_bigint_if_int(f) { - Some(s) => BigInt::hash(&s, state), + match to_nint_if_int(f) { + Some(s) => NInt::hash(&s, state), None => { if f.is_nan() { // some nan from wikipedia (not that this matters) @@ -600,7 +603,7 @@ fn consistent_hash_f64(f: f64, state: &mut H) { impl NNum { pub fn total_hash(&self, state: &mut H) { match self { - NNum::Int(a) => BigInt::hash(&a, state), + NNum::Int(a) => NInt::hash(&a, state), NNum::Rational(r) => { // TODO: should we make rationals consistent with floats? BigInt::hash(r.numer(), state); @@ -709,8 +712,8 @@ macro_rules! binary_match { (a, NNum::Float(fb)) => NNum::Float($floatmethod(a.to_f64_or_inf_or_complex().expect("complex not elim"), fb.soft_deref())), (NNum::Rational(ra), NNum::Rational(rb)) => NNum::Rational(Box::new($ratmethod(ra.soft_deref(), rb.soft_deref()))), - (NNum::Rational(ra), NNum::Int(b)) => NNum::Rational(Box::new($ratmethod(ra.soft_deref(), &BigRational::from(b.clone())))), - (NNum::Int(a), NNum::Rational(rb)) => NNum::Rational(Box::new($ratmethod(&BigRational::from(a.clone()), rb.soft_deref()))), + (NNum::Rational(ra), NNum::Int(b)) => NNum::Rational(Box::new($ratmethod(ra.soft_deref(), &BigRational::from(b.to_bigint().into_owned())))), + (NNum::Int(a), NNum::Rational(rb)) => NNum::Rational(Box::new($ratmethod(&BigRational::from(a.to_bigint().into_owned()), rb.soft_deref()))), (NNum::Int (a), NNum::Int (b)) => NNum::Int($intmethod(a, b)), } @@ -808,7 +811,7 @@ impl NNum { binary_match!( self, other, - Integer::div_floor, + NInt::div_floor, dumb_rational_div_floor, f64::div_euclid, dumb_complex_div_floor @@ -818,7 +821,7 @@ impl NNum { binary_match!( self, other, - Integer::mod_floor, + NInt::mod_floor, Rem::rem, f64::rem_euclid, Rem::rem @@ -890,7 +893,7 @@ impl Not for &NNum { type Output = NNum; fn not(self) -> NNum { - match self.to_bigint() { + match self.to_nint() { Some(n) => NNum::Int(!n), None => NNum::Float(f64::NAN), } @@ -925,7 +928,7 @@ impl Product for NNum { macro_rules! force_bi_binary_match { ($a:expr, $b:expr, $method:ident, $intmethod:expr) => { - match ($a.to_bigint(), $b.to_bigint()) { + match ($a.to_nint(), $b.to_nint()) { (Some(ia), Some(ib)) => NNum::Int($intmethod(ia, ib)), _ => NNum::Float(f64::NAN), } @@ -975,37 +978,6 @@ impl Shr for NNum { } } -pub fn lazy_is_prime(n: &BigInt) -> bool { - if n <= &BigInt::from(1) { - false - } else if n <= &BigInt::from(3) { - true - } else if (n % BigInt::from(2)).is_zero() || (n % BigInt::from(3)).is_zero() { - false - } else { - let s = n.sqrt(); // truncates - let mut f = BigInt::from(5); - loop { - if f > s { - return true; - } - if (n % &f).is_zero() { - return false; - } - - let g = &f + BigInt::from(2); - if g > s { - return true; - } - if (n % &g).is_zero() { - return false; - } - - f += 6; - } - } -} - pub fn lazy_factorize(mut a: BigInt) -> Vec<(BigInt, usize)> { let mut acc = Vec::new(); match a.sign() { @@ -1056,27 +1028,27 @@ pub fn lazy_factorize(mut a: BigInt) -> Vec<(BigInt, usize)> { impl NNum { pub fn gcd(&self, other: &NNum) -> NNum { - force_bi_binary_match!(self, other, gcd, Integer::gcd) + force_bi_binary_match!(self, other, gcd, NInt::gcd) } pub fn is_prime(&self) -> bool { match self { - NNum::Int(a) => lazy_is_prime(a), + NNum::Int(a) => a.lazy_is_prime(), NNum::Rational(a) => { if a.is_integer() { - lazy_is_prime(&a.to_integer()) + NInt::Big(a.to_integer()).lazy_is_prime() } else { false } } - NNum::Float(a) => match to_bigint_if_int(*a) { - Some(n) => lazy_is_prime(&n), + NNum::Float(a) => match to_nint_if_int(*a) { + Some(n) => n.lazy_is_prime(), None => false, }, NNum::Complex(a) => { a.im == 0.0 - && match to_bigint_if_int(a.re) { - Some(n) => lazy_is_prime(&n), + && match to_nint_if_int(a.re) { + Some(n) => n.lazy_is_prime(), None => false, } } diff --git a/src/streams.rs b/src/streams.rs index b92e8c1..46bd339 100644 --- a/src/streams.rs +++ b/src/streams.rs @@ -3,10 +3,10 @@ use std::fmt::Debug; use std::fmt::Display; use std::rc::Rc; -use num::bigint::{BigInt, Sign}; -use num::ToPrimitive; +use num::bigint::Sign; use crate::core::*; +use crate::nint::NInt; #[derive(Debug, Clone)] pub struct Repeat(pub Obj); @@ -115,7 +115,7 @@ impl Stream for Cycle { } } #[derive(Debug, Clone)] -pub struct Range(pub BigInt, pub Option, pub BigInt); +pub struct Range(pub NInt, pub Option, pub NInt); impl Range { fn empty(&self) -> bool { let Range(start, end, step) = self; @@ -171,9 +171,11 @@ impl Stream for Range { } } Sign::Minus => { - ((end - start - step + 1usize).max(BigInt::from(0)) / (-step)).to_usize() + ((end - start - step + NInt::Small(1)).max(NInt::Small(0)) / (-step)).to_usize() + } + Sign::Plus => { + ((end - start + step - NInt::Small(1)).max(NInt::Small(0)) / step).to_usize() } - Sign::Plus => ((end - start + step - 1usize).max(BigInt::from(0)) / step).to_usize(), } } } diff --git a/tests/test.rs b/tests/test.rs index 7d241cd..aee652d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2,9 +2,10 @@ extern crate noulith; // use std::rc::Rc; use noulith::simple_eval; use noulith::Obj; +use num::bigint::BigInt; -fn i(n: i32) -> Obj { - Obj::i32(n) +fn i(n: i64) -> Obj { + Obj::i64(n) } #[test] @@ -27,6 +28,23 @@ fn quick_operators() { assert_eq!(simple_eval("plus := \\x, y -> x + y; 2 plus 3"), i(5)); } +#[test] +fn big_operators() { + let a = BigInt::from(314).pow(159); + let b = BigInt::from(271).pow(828); + assert_eq!(simple_eval("314^159"), Obj::from(a.clone())); + assert_eq!(simple_eval("271^828"), Obj::from(b.clone())); + assert_eq!(simple_eval("314^159 + 271^828"), Obj::from(&a + &b)); + assert_eq!(simple_eval("314^159 - 271^828"), Obj::from(&a - &b)); + assert_eq!(simple_eval("314^159 * 271^828"), Obj::from(&a * &b)); + assert_eq!(simple_eval("314^159 // 271^828"), Obj::from(&a / &b)); + assert_eq!(simple_eval("314^159 % 271^828"), Obj::from(&a % &b)); + assert_eq!(simple_eval("314^159 & 271^828"), Obj::from(&a & &b)); + assert_eq!(simple_eval("314^159 | 271^828"), Obj::from(&a | &b)); + assert_eq!(simple_eval("314^159 ~ 271^828"), Obj::from(&a ^ &b)); + assert_eq!(simple_eval("314 ^ 159 // 314 ^ 158 == 314"), i(1)); +} + #[test] fn splat_call() { assert_eq!(simple_eval("f := \\...x -> len(x); f()"), i(0)); From c76f98e17481235f0bae5dbbf46781c6bb4b8d9a Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Tue, 24 Oct 2023 19:08:37 -0700 Subject: [PATCH 16/23] give ChainEvaluator "morally correct" types --- src/eval.rs | 135 ++++++++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 68 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 97dc104..5da3c9a 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -88,45 +88,47 @@ impl Display for EvaluatedLvalue { } pub struct ChainEvaluator { - // represents all pending operators and operands that we can't yet be certain have high enough - // precedence to evaluate. in steady state, the operators are interspersed between the operands - // in order, so operands.len() == 1 + operators.len(). but elements of operands can be multiple - // Objs long when we're chaining operators. e.g. after seeing 1 < 2 <= 3, operands should be - // [1, 2], [3] and operators should just have one element that's a (<, <=) merger - operands: Vec>, - operators: Vec<(Func, Precedence, CodeLoc, CodeLoc)>, + // represents all pending operands and operators, left to right, that we can't yet be certain + // have high enough precedence to evaluate. usually the operands vec will just have length 1, + // but will be multiple Objs long when we're chaining operators. e.g. after seeing 1 < 2 <= 3, + // pending will have a [1, 2] and (<, <=) merger, and rightmost will be 3 + pending: Vec<(Vec, Func, Precedence, CodeLoc, CodeLoc)>, + rightmost: Obj, } impl ChainEvaluator { pub fn new(operand: Obj) -> ChainEvaluator { ChainEvaluator { - operands: vec![vec![operand]], - operators: Vec::new(), + pending: Vec::new(), + rightmost: operand, } } pub fn run_top_popped( &mut self, env: &REnv, + mut operands: Vec, op: Func, start: CodeLoc, end: CodeLoc, ) -> NRes<()> { - let rhs = self.operands.pop().expect("sync"); - let mut lhs = self.operands.pop().expect("sync"); - lhs.extend(rhs); - self.operands.push(vec![add_trace( - op.run(env, lhs), + // run an operator and its LHS that were popped from pending (note that that means they + // were "popped" from the left of self.rightmost) with self.rightmost; put the result back + // into self.rightmost + operands.push(std::mem::take(&mut self.rightmost)); + self.rightmost = add_trace( + op.run(env, operands), || format!("chain {}", op), start, end, - )?]); + )?; Ok(()) } pub fn run_top(&mut self, env: &REnv) -> NRes<()> { - let (op, _, start, end) = self.operators.pop().expect("sync"); - self.run_top_popped(env, op, start, end) + let (operands, op, _, start, end) = self.pending.pop().expect("sync"); + self.run_top_popped(env, operands, op, start, end)?; + Ok(()) } pub fn give( @@ -138,70 +140,72 @@ impl ChainEvaluator { start: CodeLoc, end: CodeLoc, ) -> NRes<()> { + // add an operator and operand. note to help follow the logic: operand must end up in + // self.rightmost at the end since the next operator could always be higher precedence + // is the top operator on the stack higher precedence than the new operator? while self - .operators + .pending .last() - .map_or(false, |t| t.1.tighter_than_when_before(&precedence)) + .map_or(false, |t| t.2.tighter_than_when_before(&precedence)) { // if so, run it, and repeat. - let (top, prec, start, end) = self.operators.pop().expect("sync"); + let (mut operands, top, prec, start, end) = self.pending.pop().expect("sync"); match top.try_chain(&operator) { Some(new_op) => { // except that if it chains with the new operator, that takes precedence; merge - // the operators and the last two operands. - self.operators.push((new_op, prec, start, end)); - - let last = self.operands.pop().expect("sync"); - self.operands.last_mut().expect("sync").extend(last); - self.operands.push(vec![operand]); - + // the operators and operands. + operands.push(std::mem::replace(&mut self.rightmost, operand)); + self.pending.push((operands, new_op, prec, start, end)); return Ok(()); } None => { - self.run_top_popped(env, top, start, end)?; + self.run_top_popped(env, operands, top, start, end)?; } } } - self.operators.push((operator, precedence, start, end)); - self.operands.push(vec![operand]); + self.pending.push(( + vec![std::mem::replace(&mut self.rightmost, operand)], + operator, + precedence, + start, + end, + )); Ok(()) } - pub fn finish(&mut self, env: &REnv) -> NRes { - while !self.operators.is_empty() { + pub fn finish(mut self, env: &REnv) -> NRes { + while !self.pending.is_empty() { self.run_top(env)?; } - if self.operands.len() == 1 { - Ok(self.operands.remove(0).remove(0)) - } else { - panic!("chain eval out of sync") - } + Ok(self.rightmost) } } struct LvalueChainEvaluator { - operands: Vec>>, - operators: Vec<(Func, Precedence)>, + pending: Vec<(Vec>, Func, Precedence)>, + rightmost: Box, } impl LvalueChainEvaluator { fn new(operand: EvaluatedLvalue) -> LvalueChainEvaluator { LvalueChainEvaluator { - operands: vec![vec![Box::new(operand)]], - operators: Vec::new(), + pending: Vec::new(), + rightmost: Box::new(operand), } } - fn run_top_popped(&mut self, op: Func) -> NRes<()> { - let rhs = self.operands.pop().expect("sync"); - let mut lhs = self.operands.pop().expect("sync"); + fn run_top_popped(&mut self, mut operands: Vec>, op: Func) -> NRes<()> { + // let rhs = self.operands.pop().expect("sync"); + // let mut lhs = self.operands.pop().expect("sync"); match op { Func::Builtin(b) => { - lhs.extend(rhs); - self.operands - .push(vec![Box::new(EvaluatedLvalue::Destructure(b, lhs))]); + operands.push(std::mem::replace( + &mut self.rightmost, + Box::new(EvaluatedLvalue::Underscore), + )); + self.rightmost = Box::new(EvaluatedLvalue::Destructure(b, operands)); Ok(()) } nop => Err(NErr::type_error(format!( @@ -212,8 +216,8 @@ impl LvalueChainEvaluator { } fn run_top(&mut self) -> NRes<()> { - let (op, _) = self.operators.pop().expect("sync"); - self.run_top_popped(op) + let (operands, op, _) = self.pending.pop().expect("sync"); + self.run_top_popped(operands, op) } fn give( @@ -223,41 +227,36 @@ impl LvalueChainEvaluator { operand: EvaluatedLvalue, ) -> NRes<()> { while self - .operators + .pending .last() - .map_or(false, |t| t.1.tighter_than_when_before(&precedence)) + .map_or(false, |t| t.2.tighter_than_when_before(&precedence)) { - let (top, prec) = self.operators.pop().expect("sync"); + let (mut operands, top, prec) = self.pending.pop().expect("sync"); match top.try_chain(&operator) { Some(new_op) => { - self.operators.push((new_op, prec)); - - let last = self.operands.pop().expect("sync"); - self.operands.last_mut().expect("sync").extend(last); - self.operands.push(vec![Box::new(operand)]); - + operands.push(std::mem::replace(&mut self.rightmost, Box::new(operand))); + self.pending.push((operands, new_op, prec)); return Ok(()); } None => { - self.run_top_popped(top)?; + self.run_top_popped(operands, top)?; } } } - self.operators.push((operator, precedence)); - self.operands.push(vec![Box::new(operand)]); + self.pending.push(( + vec![std::mem::replace(&mut self.rightmost, Box::new(operand))], + operator, + precedence, + )); Ok(()) } - fn finish(&mut self) -> NRes { - while !self.operators.is_empty() { + fn finish(mut self) -> NRes { + while !self.pending.is_empty() { self.run_top()?; } - if self.operands.len() == 1 { - Ok(*self.operands.remove(0).remove(0)) - } else { - panic!("chain eval out of sync") - } + Ok(*self.rightmost) } } From 2285059a3f5797460dd59f57248b176c5cef9f57 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Tue, 24 Oct 2023 22:23:42 -0400 Subject: [PATCH 17/23] accept mere int in internal for --- src/eval.rs | 63 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 5da3c9a..62b4239 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1306,27 +1306,52 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } Expr::InternalPeek(i) => Env::try_borrow_peek(env, *i), Expr::InternalFor(iteratee, body) => { - let mut itr = evaluate(env, iteratee)?; - for x in mut_obj_into_iter(&mut itr, "internal for iteration")? { - let x = x?; - let s = { - let mut ptr = try_borrow_mut_nres(env, "internal", "for push")?; - let s = ptr.internal_stack.len(); - ptr.internal_stack.push(x); - s - }; - let ret = evaluate(env, body); - { - let mut ptr = try_borrow_mut_nres(env, "internal", "for push") - .expect("internal for: stack is out of sync, unrecoverable"); - ptr.internal_stack.truncate(s); + match evaluate(env, iteratee)? { + Obj::Seq(mut s) => { + for x in mut_seq_into_iter(&mut s) { + let x = x?; + let s = { + let mut ptr = try_borrow_mut_nres(env, "internal", "for push")?; + let s = ptr.internal_stack.len(); + ptr.internal_stack.push(x); + s + }; + let ret = evaluate(env, body); + { + let mut ptr = try_borrow_mut_nres(env, "internal", "for push") + .expect("internal for: stack is out of sync, unrecoverable"); + ptr.internal_stack.truncate(s); + } + match ret { + Err(NErr::Break(k)) => return Ok(k.unwrap_or(Obj::Null)), + Err(NErr::Continue) => (), + Err(r) => return Err(r), + Ok(_) => (), + } + } } - match ret { - Err(NErr::Break(k)) => return Ok(k.unwrap_or(Obj::Null)), - Err(NErr::Continue) => (), - Err(r) => return Err(r), - Ok(_) => (), + Obj::Num(NNum::Int(n)) => { + let mut i = NInt::Small(0); + while i < n { + let s = try_borrow_nres(env, "internal", "for check")?.internal_stack.len(); + let ret = evaluate(env, body); + try_borrow_mut_nres(env, "internal", "for end") + .expect("internal for: stack is out of sync, unrecoverable") + .internal_stack. + truncate(s); + match ret { + Err(NErr::Break(k)) => return Ok(k.unwrap_or(Obj::Null)), + Err(NErr::Continue) => (), + Err(r) => return Err(r), + Ok(_) => (), + } + i += 1; + } } + e => return Err(NErr::type_error(format!( + "{}: internal for: not int and not iterable", + FmtObj::debug(&e) + ))), } Ok(Obj::Null) } From e2e739551a772501231d52137602f2e821341b9e Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Tue, 19 Dec 2023 23:52:38 -0800 Subject: [PATCH 18/23] debug and fix backref parsing, which i broke at some point --- src/core.rs | 1 - src/lib.rs | 22 ++++++++++++++++++++++ tests/test.rs | 12 +++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/core.rs b/src/core.rs index da47d5d..81c9c00 100644 --- a/src/core.rs +++ b/src/core.rs @@ -3194,7 +3194,6 @@ impl Parser { Token::Lambda => { self.advance(); if let Some((end, us)) = self.try_consume_usize("backref")? { - self.advance(); Ok(LocExpr { start, end, diff --git a/src/lib.rs b/src/lib.rs index 28e5dcb..0217e6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ mod lex; mod nint; pub mod nnum; mod streams; +// mod optim; use crate::few::*; use crate::iter::*; use crate::streams::*; @@ -3697,6 +3698,27 @@ pub fn initialize(env: &mut Env) { started = true; write!(t.output, "{}", FmtObj::debug(arg))?; } + writeln!(t.output, "")?; + Ok(()) + }) + .map_err(|e| NErr::io_error(format!("writing {}", e)))?; + Ok(Obj::Null) + }, + }); + env.insert_builtin(BasicBuiltin { + name: "__internal_debug".to_string(), + body: |env, args| { + try_borrow_nres(env, "debug", &format!("{}", args.len()))? + .mut_top_env(|t| -> io::Result<()> { + let mut started = false; + for arg in args.iter() { + if started { + write!(t.output, " ")?; + } + started = true; + write!(t.output, "{:?}", arg)?; + } + writeln!(t.output, "")?; Ok(()) }) .map_err(|e| NErr::io_error(format!("writing {}", e)))?; diff --git a/tests/test.rs b/tests/test.rs index aee652d..dd3a117 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,7 +1,10 @@ extern crate noulith; -// use std::rc::Rc; +use std::rc::Rc; +use std::cell::RefCell; use noulith::simple_eval; use noulith::Obj; +use noulith::Env; +use noulith::{evaluate, parse}; use num::bigint::BigInt; fn i(n: i64) -> Obj { @@ -531,3 +534,10 @@ fn switch() { assert_eq!(simple_eval("struct Foo(bar, baz); switch (Foo(3, 4)) case a, b -> 0 case Foo(0, _) -> 0 case Foo(a, b) -> a + b"), i(7)); assert_eq!(simple_eval("struct Foo(bar, baz); switch (Foo(3, 4)) case a, b -> 0 case Foo(3, b) -> b case Foo(a, b) -> a + b"), i(4)); } + +#[test] +fn backrefs() { + let env = Env::empty(); + env.mut_top_env(|v| v.backrefs.push(i(1337))); + assert_eq!(evaluate(&Rc::new(RefCell::new(env)), &parse("\\1").unwrap().unwrap()).unwrap(), i(1337)); +} From 3bce693335d8170895407846c237b6dad10ef7ec Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Tue, 19 Dec 2023 23:54:24 -0800 Subject: [PATCH 19/23] wasm link --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 908f3a8..c522e83 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ An attempt to give myself a new Pareto-optimal choice for quick-and-dirty scripts, particularly when I'm not on a dev computer, and to practice writing a more realistic programming language instead of the [overengineered stack-based nonsense](https://github.com/betaveros/paradoc) I spend too much time on. ([Crafting Interpreters](https://craftinginterpreters.com/) is such a good book, I have no excuses.) +You can [try Noulith online](https://betaveros.github.io/noulith/) (via wasm)! + ## Elevator pitches (and anti-pitches) - Immutable data structures (but not variables) means you can write `matrix = [[0] ** 10] ** 10; matrix[1][2] = 3` and not worry about it, instead of the `[[0] * 10 for _ in range(10)]` you always have to do in Python. You can also freely use things as keys in dictionaries. But, thanks to mutate-or-copy-on-write shenanigans behind the scenes (powered by Rust's overpowered reference-counting pointers), you don't have to sacrifice the performance you'd get from mutating lists. (There are almost certainly space leaks from cavalier use of `Rc` but shhhhh.) From c6cd72d6d73d3d40f87809f397c2a573f567f3e0 Mon Sep 17 00:00:00 2001 From: crides Date: Thu, 7 Dec 2023 22:51:16 -0800 Subject: [PATCH 20/23] lcm --- src/lib.rs | 4 ++++ src/nint.rs | 3 +++ src/nnum.rs | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0217e6e..f527aba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2899,6 +2899,10 @@ pub fn initialize(env: &mut Env) { name: "gcd".to_string(), body: |a, b| a.gcd(&b), }); + env.insert_builtin(TwoNumsToNumsBuiltin { + name: "lcm".to_string(), + body: |a, b| a.lcm(&b), + }); env.insert_rassoc_builtin(TwoNumsToNumsBuiltin { name: "^".to_string(), body: |a, b| a.pow_num(&b), diff --git a/src/nint.rs b/src/nint.rs index 6b96754..f92fbc7 100644 --- a/src/nint.rs +++ b/src/nint.rs @@ -361,6 +361,9 @@ impl NInt { pub fn gcd(&self, other: &Self) -> NInt { NInt::Big(Integer::gcd(&*self.to_bigint(), &*other.to_bigint())) } + pub fn lcm(&self, other: &Self) -> NInt { + NInt::Big(Integer::lcm(&*self.to_bigint(), &*other.to_bigint())) + } pub fn sqrt(&self) -> NInt { NInt::Big(self.to_bigint().sqrt()) } diff --git a/src/nnum.rs b/src/nnum.rs index 065eadd..fee0261 100644 --- a/src/nnum.rs +++ b/src/nnum.rs @@ -1031,6 +1031,10 @@ impl NNum { force_bi_binary_match!(self, other, gcd, NInt::gcd) } + pub fn lcm(&self, other: &NNum) -> NNum { + force_bi_binary_match!(self, other, gcd, NInt::lcm) + } + pub fn is_prime(&self) -> bool { match self { NNum::Int(a) => a.lazy_is_prime(), From 349e37a0cb68f3f42e508964d1ff7b82a6ddfb03 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 1 Jan 2024 00:06:28 -0800 Subject: [PATCH 21/23] random dict stuff and HOFs --- src/core.rs | 6 ++ src/eval.rs | 31 ++++++++--- src/lib.rs | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ src/streams.rs | 70 ++++++++++++++++++++++++ tests/test.rs | 71 ++++++++++++++++++++++-- 5 files changed, 311 insertions(+), 12 deletions(-) diff --git a/src/core.rs b/src/core.rs index 81c9c00..bb581b6 100644 --- a/src/core.rs +++ b/src/core.rs @@ -4068,6 +4068,9 @@ pub enum Func { Parallel(Vec), // a... -> [f1(a...), f2(a...), ...] Fanout(Vec), + // a... -> f(g1(a...), g2(a...), ...) + // where, if g_i isn't a function, it's constant + OnFanoutConst(Box, Vec), // \x, y: f(y, x) (...I feel like we shouldn't special-case so many but shrug...) Flip(Box), // each Err is a slot for an argument, true if splat @@ -4394,6 +4397,9 @@ impl Display for Func { write!(formatter, "Parallel({})", CommaSeparated(fs)) } Func::Fanout(fs) => write!(formatter, "Fanout({})", CommaSeparated(fs)), + Func::OnFanoutConst(f, gs) => { + write!(formatter, "OnFanoutConst({} . {})", f, CommaSeparated(gs)) + } Func::Flip(f) => write!(formatter, "Flip({})", f), // TODO Func::ListSection(xs) => { diff --git a/src/eval.rs b/src/eval.rs index 62b4239..a87f606 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1333,12 +1333,14 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Obj::Num(NNum::Int(n)) => { let mut i = NInt::Small(0); while i < n { - let s = try_borrow_nres(env, "internal", "for check")?.internal_stack.len(); + let s = try_borrow_nres(env, "internal", "for check")? + .internal_stack + .len(); let ret = evaluate(env, body); try_borrow_mut_nres(env, "internal", "for end") - .expect("internal for: stack is out of sync, unrecoverable") - .internal_stack. - truncate(s); + .expect("internal for: stack is out of sync, unrecoverable") + .internal_stack + .truncate(s); match ret { Err(NErr::Break(k)) => return Ok(k.unwrap_or(Obj::Null)), Err(NErr::Continue) => (), @@ -1348,10 +1350,12 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { i += 1; } } - e => return Err(NErr::type_error(format!( - "{}: internal for: not int and not iterable", - FmtObj::debug(&e) - ))), + e => { + return Err(NErr::type_error(format!( + "{}: internal for: not int and not iterable", + FmtObj::debug(&e) + ))) + } } Ok(Obj::Null) } @@ -2563,6 +2567,16 @@ impl Func { } Ok(Obj::list(res)) } + Func::OnFanoutConst(f, gs) => { + let mut mapped_args = Vec::new(); + for g in gs { + mapped_args.push(match g { + Obj::Func(gf, _) => gf.run(env, args.clone())?, + x => x.clone(), + }); + } + f.run(env, mapped_args) + } Func::Flip(f) => match few2(args) { // weird lol Few2::One(a) => Ok(Obj::Func(Func::PartialApp1(f.clone(), Box::new(a)), Precedence::zero())), @@ -2696,6 +2710,7 @@ impl Func { Func::OnComposition(..) => None, Func::Parallel(_) => None, Func::Fanout(_) => None, + Func::OnFanoutConst(..) => None, Func::Flip(..) => None, Func::ListSection(_) => None, Func::ChainSection(..) => None, diff --git a/src/lib.rs b/src/lib.rs index f527aba..44f2a8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1454,6 +1454,38 @@ impl Builtin for Fanout { } } +#[derive(Debug, Clone)] +struct LiftedEquals; + +impl Builtin for LiftedEquals { + fn run(&self, _env: &REnv, args: Vec) -> NRes { + Ok(Obj::Func( + Func::OnFanoutConst( + Box::new(Func::Builtin(Rc::new(ComparisonOperator::of( + "==", + |a, b| Ok(a == b), + )))), + args, + ), + Precedence::zero(), + )) + } + + fn builtin_name(&self) -> &str { + "equals" + } + + fn try_chain(&self, other: &Func) -> Option { + match other { + Func::Builtin(b) => match b.builtin_name() { + "equals" => Some(Func::Builtin(Rc::new(self.clone()))), + _ => None, + }, + _ => None, + } + } +} + // not actually chainable, but conditional partial application, and also I want aliases #[derive(Debug, Clone)] pub struct Group { @@ -3315,6 +3347,15 @@ pub fn initialize(env: &mut Env) { (a, b) => Err(NErr::argument_error_2(&a, &b)), }, }); + env.insert_builtin(EnvTwoArgBuiltin { + name: "lazy_filter".to_string(), + body: |env, a, b| match (a, b) { + (Obj::Seq(Seq::Stream(s)), Obj::Func(b, _)) => Ok(Obj::Seq(Seq::Stream(Rc::new( + FilteredStream(Ok((s.clone_box(), b, Rc::clone(env)))), + )))), + (a, b) => Err(NErr::argument_error_2(&a, &b)), + }, + }); env.insert_builtin(EnvTwoArgBuiltin { name: "each".to_string(), body: |env, mut a, b| { @@ -3374,6 +3415,53 @@ pub fn initialize(env: &mut Env) { _ => Err(NErr::type_error("not dict or callable".to_string())), }, }); + env.insert_builtin(EnvTwoArgBuiltin { + name: "filter_keys".to_string(), + body: |env, a, b| match (a, b) { + (Obj::Seq(Seq::Dict(d, def)), Obj::Func(b, _)) => { + // don't see an easy functional way to do this given fallible predicate + let mut m = HashMap::new(); + for (k, v) in d.iter() { + if b.run1(env, key_to_obj(k.clone()))?.truthy() { + m.insert(k.clone(), v.clone()); + } + } + Ok(Obj::dict(m, def.map(|x| *x))) + } + _ => Err(NErr::type_error("not dict or callable".to_string())), + }, + }); + env.insert_builtin(EnvTwoArgBuiltin { + name: "filter_values".to_string(), + body: |env, a, b| match (a, b) { + (Obj::Seq(Seq::Dict(d, def)), Obj::Func(b, _)) => { + let mut m = HashMap::new(); + for (k, v) in d.iter() { + if b.run1(env, v.clone())?.truthy() { + m.insert(k.clone(), v.clone()); + } + } + Ok(Obj::dict(m, def.map(|x| *x))) + } + _ => Err(NErr::type_error("not dict or callable".to_string())), + }, + }); + env.insert_builtin(EnvTwoArgBuiltin { + name: "filter_items".to_string(), + body: |env, a, b| match (a, b) { + (Obj::Seq(Seq::Dict(d, def)), Obj::Func(b, _)) => { + // don't see an easy functional way to do this given fallible predicate + let mut m = HashMap::new(); + for (k, v) in d.iter() { + if b.run2(env, key_to_obj(k.clone()), v.clone())?.truthy() { + m.insert(k.clone(), v.clone()); + } + } + Ok(Obj::dict(m, def.map(|x| *x))) + } + _ => Err(NErr::type_error("not dict or callable".to_string())), + }, + }); env.insert_builtin(OneArgBuiltin { name: "flatten".to_string(), body: |mut a| { @@ -3453,6 +3541,7 @@ pub fn initialize(env: &mut Env) { env.insert_builtin(ZipLongest); env.insert_builtin(Parallel); env.insert_builtin(Fanout); + env.insert_builtin(LiftedEquals); env.insert_builtin(EnvOneArgBuiltin { name: "transpose".to_string(), body: |_env, a| { @@ -4341,6 +4430,62 @@ pub fn initialize(env: &mut Env) { (a, b) => Err(NErr::argument_error_2(&a, &b)), }, }); + env.insert_builtin(TwoArgBuiltin { + name: "||+".to_string(), + body: |a, b| match (a, b) { + (Obj::Seq(Seq::Dict(mut a, d)), Obj::Seq(Seq::Dict(b, _))) => { + let m = Rc::make_mut(&mut a); + for (k, v) in unwrap_or_clone(b) { + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + let slot = e.get_mut(); + *slot = expect_nums_and_vectorize_2_nums( + |a, b| a + b, + std::mem::take(slot), + v, + "||+", + )? + } + } + } + Ok(Obj::Seq(Seq::Dict(a, d))) + } + (a, b) => Err(NErr::argument_error_2(&a, &b)), + }, + }); + env.insert_builtin(TwoArgBuiltin { + name: "||-".to_string(), + body: |a, b| match (a, b) { + (Obj::Seq(Seq::Dict(mut a, d)), Obj::Seq(Seq::Dict(b, _))) => { + let m = Rc::make_mut(&mut a); + for (k, v) in unwrap_or_clone(b) { + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(expect_nums_and_vectorize_1( + |a| Ok(Obj::Num(-a)), + v, + "||- unary", + )?); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + let slot = e.get_mut(); + *slot = expect_nums_and_vectorize_2_nums( + |a, b| a - b, + std::mem::take(slot), + v, + "||-", + )? + } + } + } + Ok(Obj::Seq(Seq::Dict(a, d))) + } + (a, b) => Err(NErr::argument_error_2(&a, &b)), + }, + }); env.insert_builtin(TwoArgBuiltin { name: "|.".to_string(), body: |a, b| match (a, b) { diff --git a/src/streams.rs b/src/streams.rs index 46bd339..62958be 100644 --- a/src/streams.rs +++ b/src/streams.rs @@ -636,3 +636,73 @@ impl Stream for MappedStream { } */ } + +// i think just equally illegal +// again we'll treat NErr::Break as graceful termination +pub struct FilteredStream(pub NRes<(Box, Func, REnv)>); +impl Clone for FilteredStream { + fn clone(&self) -> FilteredStream { + match &self.0 { + Err(e) => FilteredStream(Err(e.clone())), + Ok((inner, func, renv)) => { + FilteredStream(Ok((inner.clone_box(), func.clone(), renv.clone()))) + } + } + } +} +// directly debug-printing env can easily recurse infinitely +impl Debug for FilteredStream { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match &self.0 { + Err(NErr::Break(None)) => write!(fmt, "FilteredStream(stopped)"), + Err(e) => write!(fmt, "FilteredStream(ERROR: {:?})", e), + Ok((inner, func, _)) => write!(fmt, "FilteredStream({:?}, {:?}, ...)", inner, func), + } + } +} +impl Iterator for FilteredStream { + type Item = NRes; + fn next(&mut self) -> Option> { + let (inner, func, renv) = self.0.as_mut().ok()?; + loop { + match inner.next() { + Some(Err(e)) => { + self.0 = Err(e.clone()); + return Some(Err(e)); + } + Some(Ok(cur)) => match func.run1(&renv, cur.clone()) { + Ok(nxt) => { + if nxt.truthy() { + return Some(Ok(cur)); + } + } + Err(e) => { + self.0 = Err(e.clone()); + return Some(Err(e)); + } + }, + None => { + self.0 = Err(NErr::Break(None)); + return None; + } + } + } + } +} +impl Display for FilteredStream { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match &self.0 { + Ok((inner, func, _)) => write!(formatter, "FilteredStream({}, {}, ...)", inner, func), + Err(e) => write!(formatter, "FilteredStream(ERROR: {})", e), + } + } +} +impl Stream for FilteredStream { + fn peek(&self) -> Option> { + // lol this can be arbitrarily slow: + self.clone().next() + } + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} diff --git a/tests/test.rs b/tests/test.rs index dd3a117..9f67bd3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,11 +1,11 @@ extern crate noulith; -use std::rc::Rc; -use std::cell::RefCell; use noulith::simple_eval; -use noulith::Obj; use noulith::Env; +use noulith::Obj; use noulith::{evaluate, parse}; use num::bigint::BigInt; +use std::cell::RefCell; +use std::rc::Rc; fn i(n: i64) -> Obj { Obj::i64(n) @@ -129,6 +129,14 @@ fn math() { assert_eq!(simple_eval("7 %% 4"), i(3)); } +#[test] +fn number_theory() { + assert_eq!(simple_eval("is_prime 35"), i(0)); + assert_eq!(simple_eval("is_prime 37"), i(1)); + assert_eq!(simple_eval("6 lcm 10"), i(30)); + assert_eq!(simple_eval("6 gcd 10"), i(2)); +} + #[test] fn fractions() { assert_eq!(simple_eval("numerator! 35 / 28"), i(5)); @@ -288,6 +296,17 @@ fn dicts() { assert_eq!(simple_eval("2 ∉ {1: 2}"), i(1)); } +#[test] +fn more_dicts() { + assert_eq!(simple_eval("({'a': 1, 'b': 2} || {'a': 4, 'b': 8})['a']"), i(4)); + assert_eq!(simple_eval("({'a': 1, 'b': 2} ||+ {'a': 4, 'b': 8})['a']"), i(5)); + assert_eq!(simple_eval("({'a': 1, 'b': 2} ||- {'a': 4, 'b': 8})['a']"), i(-3)); + assert_eq!(simple_eval("({'a': 1, 'b': 2} ||+ {'A': 4, 'b': 8})['a']"), i(1)); + assert_eq!(simple_eval("({'a': 1, 'b': 2} ||- {'A': 4, 'b': 8})['a']"), i(1)); + assert_eq!(simple_eval("({'A': 1, 'b': 2} ||+ {'a': 4, 'b': 8})['a']"), i(4)); + assert_eq!(simple_eval("({'A': 1, 'b': 2} ||- {'a': 4, 'b': 8})['a']"), i(-4)); +} + #[test] fn fast_append_pop() { assert_eq!( @@ -426,6 +445,47 @@ fn basic_hofs() { ); } +#[test] +fn more_hofs() { + assert_eq!( + simple_eval("1 to 10 filter ((%3) equals 2) then unwords"), + Obj::from("2 5 8") + ); +} + +#[test] +fn dict_hofs() { + assert_eq!( + simple_eval("set(1 to 10) map_keys (//2) then sort then unwords"), + Obj::from("0 1 2 3 4 5") + ); + assert_eq!( + simple_eval("(for (k <- 1 to 5) yield k: k) map_values (//2) then items then sort then flatten then unwords"), + Obj::from("1 0 2 1 3 1 4 2 5 2") + ); + assert_eq!( + simple_eval("{1: 2, 2: 3, 3: 4} filter_keys (>2) then items then flatten then unwords"), + Obj::from("3 4") + ); + assert_eq!( + simple_eval("{1: 2, 2: 3, 3: 4} filter_values (<3) then items then flatten then unwords"), + Obj::from("1 2") + ); +} + +#[test] +fn lazy_hofs() { + assert_eq!( + simple_eval("iota(1) lazy_map (*2) take 4 then unwords"), + Obj::from("2 4 6 8") + ); + assert_eq!( + simple_eval("iota(1) lazy_filter even take 4 then unwords"), + Obj::from("2 4 6 8") + ); +} + + #[test] fn folds() { assert_eq!(simple_eval("1 to 10 fold +"), i(55)); @@ -539,5 +599,8 @@ fn switch() { fn backrefs() { let env = Env::empty(); env.mut_top_env(|v| v.backrefs.push(i(1337))); - assert_eq!(evaluate(&Rc::new(RefCell::new(env)), &parse("\\1").unwrap().unwrap()).unwrap(), i(1337)); + assert_eq!( + evaluate(&Rc::new(RefCell::new(env)), &parse("\\1").unwrap().unwrap()).unwrap(), + i(1337) + ); } From 2800b02e4a04d7477e23bcdb00eee4710beaf9ca Mon Sep 17 00:00:00 2001 From: crides Date: Sun, 25 Dec 2022 17:30:47 -0600 Subject: [PATCH 22/23] |.. on list --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 44f2a8a..0d993ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4536,6 +4536,16 @@ pub fn initialize(env: &mut Env) { env.insert_builtin(TwoArgBuiltin { name: "|..".to_string(), body: |a, b| match (a, b) { + (Obj::Seq(Seq::List(mut a)), Obj::Seq(mut s)) => { + let mut it = mut_seq_into_iter(&mut s); + match (it.next(), it.next(), it.next()) { + (Some(k), Some(v), None) => { + Rc::make_mut(&mut a)[obj_clamp_to_usize_ok(&k?.clone())?] = v?.clone(); + Ok(Obj::Seq(Seq::List(a))) + } + _ => Err(NErr::argument_error("RHS must be pair".to_string())), + } + } (Obj::Seq(Seq::Dict(mut a, d)), Obj::Seq(mut s)) => { let mut it = mut_seq_into_iter(&mut s); match (it.next(), it.next(), it.next()) { From db5beaa557227fa159b600d2aec18663a3c21f10 Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Mon, 1 Jan 2024 00:23:12 -0800 Subject: [PATCH 23/23] `|..`: use pythonic index and test --- src/lib.rs | 3 ++- tests/test.rs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0d993ca..ac145f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4540,7 +4540,8 @@ pub fn initialize(env: &mut Env) { let mut it = mut_seq_into_iter(&mut s); match (it.next(), it.next(), it.next()) { (Some(k), Some(v), None) => { - Rc::make_mut(&mut a)[obj_clamp_to_usize_ok(&k?.clone())?] = v?.clone(); + let i = pythonic_index(&a, &k?.clone())?; + Rc::make_mut(&mut a)[i] = v?.clone(); Ok(Obj::Seq(Seq::List(a))) } _ => Err(NErr::argument_error("RHS must be pair".to_string())), diff --git a/tests/test.rs b/tests/test.rs index 9f67bd3..9544063 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -268,6 +268,8 @@ fn indexing() { simple_eval("x := [[0] ** 10] ** 10; x[1][2] = 3; x[2][2] = 4; x[1][2] $ x[2][2]"), Obj::from("34") ); + assert_eq!(simple_eval("[1, 2, 3] |.. 1 .. 4 then unwords"), Obj::from("1 4 3")); + assert_eq!(simple_eval("[1, 2, 3] |.. (-1) .. 4 then unwords"), Obj::from("1 2 4")); } #[test]