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 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.) diff --git a/pkg/noulith.js b/pkg/noulith.js index ac1cd94..a3a67f9 100644 --- a/pkg/noulith.js +++ b/pkg/noulith.js @@ -236,6 +236,12 @@ function getImports() { imports.wbg.__wbindgen_object_drop_ref = function(arg0) { takeObject(arg0); }; + imports.wbg.__wbg_randomFillSync_91e2b39becca6147 = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2)); + }, arguments) }; + imports.wbg.__wbg_getRandomValues_b14734aa289bc356 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).getRandomValues(getObject(arg1)); + }, arguments) }; imports.wbg.__wbg_process_e56fd54cf6319b6c = function(arg0) { const ret = getObject(arg0).process; return addHeapObject(ret); @@ -257,14 +263,6 @@ function getImports() { const ret = typeof(getObject(arg0)) === 'string'; return ret; }; - imports.wbg.__wbg_static_accessor_NODE_MODULE_26b231378c1be7dd = function() { - const ret = module; - return addHeapObject(ret); - }; - imports.wbg.__wbg_require_0db1598d9ccecb30 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).require(getStringFromWasm0(arg1, arg2)); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbg_crypto_b95d7173266618a9 = function(arg0) { const ret = getObject(arg0).crypto; return addHeapObject(ret); @@ -273,11 +271,13 @@ function getImports() { const ret = getObject(arg0).msCrypto; return addHeapObject(ret); }; - imports.wbg.__wbg_getRandomValues_b14734aa289bc356 = function() { return handleError(function (arg0, arg1) { - getObject(arg0).getRandomValues(getObject(arg1)); - }, arguments) }; - imports.wbg.__wbg_randomFillSync_91e2b39becca6147 = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2)); + imports.wbg.__wbg_static_accessor_NODE_MODULE_26b231378c1be7dd = function() { + const ret = module; + return addHeapObject(ret); + }; + imports.wbg.__wbg_require_0db1598d9ccecb30 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).require(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); }, arguments) }; imports.wbg.__wbg_newnoargs_971e9a5abe185139 = function(arg0, arg1) { const ret = new Function(getStringFromWasm0(arg0, arg1)); diff --git a/pkg/noulith_bg.wasm b/pkg/noulith_bg.wasm index e367cf5..4afad09 100644 Binary files a/pkg/noulith_bg.wasm and b/pkg/noulith_bg.wasm differ diff --git a/src/core.rs b/src/core.rs index 5e6484b..bb581b6 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,13 +552,17 @@ 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 +575,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 +585,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 +598,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 +614,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 /* @@ -633,27 +638,31 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { None, )), }, - ObjType::Type => Ok(Obj::Func( - Func::Type(type_of(&expect_one(arg, "type")?)), - Precedence::zero(), + 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(), )), + } +} + +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())?), } } @@ -728,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 { @@ -765,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 { @@ -806,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())) @@ -977,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))) @@ -988,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) } } @@ -1757,6 +1778,7 @@ pub enum CallSyntax { #[derive(Debug)] pub enum Expr { Null, + IntLit64(i64), IntLit(BigInt), RatLit(BigRational), FloatLit(f64), @@ -1810,6 +1832,33 @@ pub enum Expr { // shouldn't stay in the tree: CommaSeq(Vec>), Splat(Box), + + // hic sunt dracones + InternalFrame(Box), + InternalPush(Box), + InternalPop, + InternalPeek(usize), // from rear + // for each element, push it, run the body, then restore stack length + InternalFor(Box, Box), + InternalCall(usize, Box), + InternalLambda(Rc), +} + +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)), + 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)] @@ -1824,6 +1873,7 @@ pub enum Lvalue { Destructure(Box, Vec>), ChainDestructure(Box, Vec<(Box, Box)>), Literally(Box), + InternalPeek(usize), } impl Lvalue { @@ -1841,6 +1891,7 @@ impl Lvalue { lv.any_literals() || vs.iter().any(|e| e.1.any_literals()) } Lvalue::Literally(_) => true, + Lvalue::InternalPeek(_) => false, } } @@ -1879,6 +1930,7 @@ impl Lvalue { s } Lvalue::Literally(_) => HashSet::new(), + Lvalue::InternalPeek(_) => HashSet::new(), } } } @@ -1920,6 +1972,16 @@ fn to_archetypes(lvalue: &Lvalue) -> Vec { pub trait Builtin: Debug { fn run(&self, env: &REnv, args: Vec) -> NRes; + // 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]) + } + 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; @@ -2023,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)))), @@ -2043,6 +2106,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, @@ -2100,8 +2164,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, @@ -2227,6 +2291,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)), } } @@ -2259,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())), @@ -2307,12 +2373,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 +2426,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)?, @@ -2542,6 +2641,17 @@ 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::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(iteratee, body) => Ok(Expr::InternalFor( + 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)?))), }?, }) } @@ -2744,6 +2854,25 @@ 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, @@ -2765,7 +2894,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) @@ -2794,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) => { @@ -2985,7 +3117,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 => { @@ -2999,7 +3131,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, @@ -3018,7 +3150,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, @@ -3034,7 +3166,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")?; } } @@ -3048,10 +3180,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, @@ -3061,95 +3193,90 @@ 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")? { + 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), - }) - } - 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(); + 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(); - 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: ->".to_string())?; - 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: ->".to_string())?; - 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 => { 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()?))) @@ -3164,7 +3291,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()?); @@ -3204,9 +3331,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, @@ -3216,13 +3343,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))); } @@ -3238,9 +3365,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, @@ -3253,7 +3380,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())); @@ -3267,7 +3394,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), @@ -3278,6 +3405,78 @@ 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 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, + }) + } + Token::InternalPeek => { + self.advance(); + 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")?; + 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(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")?; + Ok(LocExpr { + start, + end: s.end, + expr: Expr::InternalLambda(Rc::new(s)), + }) + } _ => Err(self.error_here(format!("atom: Unexpected"))), } } else { @@ -3336,7 +3535,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, @@ -3355,8 +3554,7 @@ impl Parser { }; } else { let c = self.single("slice end")?; - let end = - self.require(Token::RightBracket, "index expr".to_string())?; + let end = self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index( Box::new(cur), @@ -3380,8 +3578,7 @@ impl Parser { }; } else { let cc = self.single("slice end")?; - let end = - self.require(Token::RightBracket, "index expr".to_string())?; + let end = self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index( Box::new(cur), @@ -3392,8 +3589,7 @@ impl Parser { }; } } else { - let end = - self.require(Token::RightBracket, "index expr".to_string())?; + let end = self.require(Token::RightBracket, "index expr")?; cur = LocExpr { expr: Expr::Index(Box::new(cur), IndexOrSlice::Index(Box::new(c))), start, @@ -3721,7 +3917,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; @@ -3857,6 +4053,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) @@ -3871,16 +4068,20 @@ 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 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>, @@ -3932,6 +4133,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 { @@ -4015,10 +4217,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, @@ -4042,6 +4249,7 @@ impl Env { Env { vars: HashMap::new(), parent: Err(Rc::new(RefCell::new(top))), + internal_stack: Vec::new(), } } // ??? @@ -4056,6 +4264,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 { @@ -4092,6 +4301,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, @@ -4105,6 +4330,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) => { @@ -4159,6 +4385,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) => { @@ -4170,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 34c6a2e..a87f606 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; @@ -28,6 +29,7 @@ pub enum EvaluatedLvalue { Literal(Obj), Destructure(Rc, Vec>), DestructureStruct(Struct, Vec>), + InternalPeek(usize), } impl Display for EvaluatedLvalue { @@ -78,50 +80,55 @@ impl Display for EvaluatedLvalue { } write!(formatter, ")") } + EvaluatedLvalue::InternalPeek(i) => { + write!(formatter, "__internal_peek({})", i) + } } } } 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), - format!("chain {}", op), + // 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( @@ -133,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!( @@ -207,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( @@ -218,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) } } @@ -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)), } } @@ -483,6 +488,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)), @@ -506,7 +512,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, ), @@ -548,16 +554,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), @@ -570,7 +576,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, ) @@ -620,6 +626,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.run2(env, 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() { @@ -669,7 +699,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, )?; @@ -686,12 +716,12 @@ 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), 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)?, @@ -700,6 +730,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( @@ -725,7 +763,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { expr.end, )), }, - format!("pop"), + || "pop".to_string(), expr.start, expr.end, ), @@ -780,7 +818,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, ), @@ -806,8 +844,8 @@ 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()])), - format!("op({})-assign", ff), + modify_every(env, &p, &mut |x| ff.run2(env, x, res.clone())), + || format!("op({})-assign", ff), expr.start, expr.end, )?; @@ -836,14 +874,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])?; + let fres = ff.run2(env, pv, res)?; add_trace( assign(&env, &p, None, fres), - format!("op({})-assign", ff), + || format!("op({})-assign", ff), expr.start, expr.end, )?; @@ -870,8 +908,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 @@ -943,14 +987,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, )?; @@ -963,14 +1007,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, ) @@ -978,7 +1022,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, ), @@ -1055,7 +1099,7 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { } } }, - "for loop".to_string(), + || "for loop".to_string(), expr.start, expr.end, ) @@ -1066,7 +1110,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, )? @@ -1076,7 +1120,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, ) { @@ -1090,7 +1134,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, )?; @@ -1107,7 +1151,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, ); @@ -1120,7 +1164,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, ) @@ -1221,7 +1265,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, ) @@ -1237,6 +1281,116 @@ 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())), + Expr::InternalFrame(e) => { + 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 + .truncate(n); + r + } + Expr::InternalPeek(i) => Env::try_borrow_peek(env, *i), + Expr::InternalFor(iteratee, body) => { + 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(_) => (), + } + } + } + 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) + } + 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(), + )), } } @@ -1257,7 +1411,7 @@ impl Closure { || Ok(args), "Wrong number of arguments", ), - "argument receiving".to_string(), + || "argument receiving".to_string(), self.body.start, self.body.end, )?; @@ -1265,7 +1419,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, ), @@ -1350,7 +1504,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), @@ -1366,7 +1520,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), _)) => { @@ -1468,6 +1622,27 @@ pub fn eval_lvalue_as_obj(env: &REnv, expr: &EvaluatedLvalue) -> NRes { ))) } } + EvaluatedLvalue::InternalPeek(i) => Env::try_borrow_peek(env, *i), + } +} + +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 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) + ))), } } @@ -1973,7 +2148,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) @@ -2100,6 +2275,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), } } @@ -2127,14 +2303,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(), @@ -2149,6 +2327,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), } } @@ -2228,6 +2407,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), } } @@ -2261,10 +2441,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))?; @@ -2300,6 +2479,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(()) + } } } @@ -2308,23 +2493,42 @@ 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.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) } @@ -2335,7 +2539,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))), @@ -2350,7 +2554,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)?); } } } @@ -2363,10 +2567,20 @@ 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())), - 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) => { @@ -2427,13 +2641,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) } @@ -2466,6 +2684,20 @@ 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]), + } + } + 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 { @@ -2478,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, @@ -2487,6 +2720,7 @@ impl Func { Func::Type(_) => None, Func::StructField(..) => None, Func::Memoized(..) => None, + Func::InternalLambda(_) => None, } } } @@ -2508,7 +2742,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/lex.rs b/src/lex.rs index 243c77a..1af8dc6 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -62,6 +62,14 @@ pub enum Token { Literally, Underscore, + InternalFrame, + InternalPush, + InternalPop, + InternalPeek, + InternalFor, + InternalCall, + InternalLambda, + // strip me before parsing Comment(String), // Newline, @@ -429,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 == '?' @@ -527,6 +539,13 @@ 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, + "__internal_call" => Token::InternalCall, + "__internal_lambda" => Token::InternalLambda, _ => Token::Ident(acc), }) } diff --git a/src/lib.rs b/src/lib.rs index d38f919..ac145f3 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,8 +54,10 @@ pub mod few; mod gamma; mod iter; mod lex; +mod nint; pub mod nnum; mod streams; +// mod optim; use crate::few::*; use crate::iter::*; use crate::streams::*; @@ -65,6 +65,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" @@ -75,21 +76,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(|a, b| Ok(Obj::Num(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 { "+" @@ -129,12 +136,16 @@ 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(|a, b| Ok(Obj::Num(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)), "-"), } } + 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 { "-" @@ -171,13 +182,17 @@ 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() ))), } } + // 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 { "*" @@ -223,7 +238,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() @@ -338,7 +353,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); } @@ -529,7 +544,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 } } { @@ -546,7 +561,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 } } { @@ -585,7 +600,7 @@ impl Catamorphism for CataCounter { Ok(()) } fn finish(&mut self) -> NRes { - Ok(Obj::from(self.0)) + Ok(Obj::usize(self.0)) } } @@ -604,7 +619,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) => { @@ -613,7 +628,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; } } @@ -626,7 +641,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"), } @@ -692,7 +707,7 @@ impl Catamorphism for CataCountDistinct { Ok(()) } fn finish(&mut self) -> NRes { - Ok(Obj::from(self.0.len())) + Ok(Obj::usize(self.0.len())) } } @@ -702,7 +717,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::>>()? @@ -733,12 +748,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))) => { @@ -757,9 +772,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"), @@ -788,16 +803,36 @@ 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)) => { - let n1 = into_bigint_ok(a)?; - let n2 = into_bigint_ok(b)?; + Few3::Two(a, b) => self.run2(_env, a, b), + Few3::Three(Obj::Num(a), Obj::Num(b), Obj::Num(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 + 1usize), - BigInt::from(1), + Some(if n3.is_negative() { + n2 - NInt::Small(1) + } else { + n2 + NInt::Small(1) + }), + n3, ))))) } - Few3::Two(Obj::Seq(Seq::String(a)), Obj::Seq(Seq::String(b))) => { + 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_nint_ok(a)?; + let n2 = into_nint_ok(b)?; + Ok(Obj::Seq(Seq::Stream(Rc::new(Range( + n1, + Some(n2 + NInt::Small(1)), + NInt::Small(1), + ))))) + } + (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()) { @@ -816,22 +851,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"), } } @@ -966,7 +987,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) } @@ -1108,7 +1129,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) } @@ -1123,7 +1144,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) } @@ -1166,7 +1187,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)) @@ -1182,7 +1203,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)) @@ -1255,7 +1276,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)?; } }, } @@ -1337,7 +1358,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); @@ -1433,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 { @@ -1534,17 +1587,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 { @@ -1563,7 +1610,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, @@ -1571,24 +1618,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 } } @@ -1621,8 +1656,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?) { @@ -1633,11 +1684,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), @@ -1645,7 +1701,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), } } @@ -1673,14 +1729,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 { @@ -1700,7 +1753,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, @@ -1708,6 +1761,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 @@ -1716,14 +1772,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 { @@ -1760,6 +1812,81 @@ 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) { + 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, + f.len() + ))), + } + } + 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 + } +} + #[derive(Debug, Clone)] pub struct TwoNumsBuiltin { name: String, @@ -1806,15 +1933,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, @@ -1822,6 +1942,18 @@ 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 @@ -1854,7 +1986,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)?; @@ -1882,7 +2014,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, @@ -1945,7 +2077,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!( @@ -1978,19 +2110,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); } } } @@ -2114,7 +2243,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) } } @@ -2129,7 +2258,7 @@ fn sorted_by>(env: &REnv, f: Func, mut v: Vec) -> NRes match ncmp(&k, &Obj::zero()) { Ok(ord) => ord, Err(e) => { @@ -2259,7 +2388,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); @@ -2297,7 +2426,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)); @@ -2315,7 +2444,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); @@ -2350,7 +2479,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(); } } @@ -2403,7 +2532,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) => { @@ -2432,7 +2561,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()?))), } @@ -2510,13 +2639,10 @@ 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.run(env, vec![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.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)) @@ -2710,35 +2836,33 @@ 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(|a, b| Ok(Obj::Num(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())), }, }); // 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))); @@ -2761,9 +2885,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(), @@ -2803,21 +2927,25 @@ 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_builtin(TwoNumsToNumsBuiltin { + name: "lcm".to_string(), + body: |a, b| a.lcm(&b), + }); + 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 { @@ -2826,16 +2954,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), ); @@ -2975,11 +3103,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(), @@ -2987,7 +3111,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(), )) }, @@ -3000,7 +3124,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())) @@ -3025,7 +3149,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!( @@ -3052,7 +3176,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(), @@ -3108,15 +3232,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(), @@ -3165,7 +3289,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)), }, @@ -3223,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| { @@ -3230,7 +3363,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) } @@ -3245,8 +3378,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?])) - .collect::>>()?, + it.map(|e| b.run1(env, e?)).collect::>>()?, )), _ => Err(NErr::type_error("not callable".to_string())), } @@ -3260,7 +3392,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), )), @@ -3273,16 +3405,63 @@ 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, }, )), _ => 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| { @@ -3324,7 +3503,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?); } @@ -3347,7 +3526,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); @@ -3362,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| { @@ -3412,7 +3592,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) @@ -3497,7 +3677,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())), ))) }, @@ -3611,6 +3791,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)))?; @@ -4008,7 +4209,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), @@ -4121,7 +4325,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); } } @@ -4137,7 +4341,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); } } @@ -4152,8 +4356,8 @@ 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() { - return Ok(Obj::from(i)); + if f.run1(env, x?.clone())?.truthy() { + return Ok(Obj::usize(i)); } } Err(NErr::value_error("didn't find".to_string())) @@ -4161,7 +4365,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())), } } @@ -4169,7 +4373,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())) @@ -4182,8 +4386,8 @@ 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() { - return Ok(Obj::from(i)); + if f.run1(env, x?.clone())?.truthy() { + return Ok(Obj::usize(i)); } } Ok(Obj::Null) @@ -4191,7 +4395,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), } } @@ -4199,7 +4403,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) @@ -4226,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) { @@ -4276,6 +4536,17 @@ 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) => { + 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())), + } + } (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()) { @@ -4331,7 +4602,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::>>()?, )) }, @@ -4439,7 +4710,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())), } @@ -4478,7 +4749,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())), } @@ -4515,11 +4786,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; } @@ -4542,7 +4810,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 { @@ -5033,9 +5301,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)), }, }); @@ -5217,6 +5485,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/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/nint.rs b/src/nint.rs new file mode 100644 index 0000000..f92fbc7 --- /dev/null +++ b/src/nint.rs @@ -0,0 +1,455 @@ +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 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()) + } + 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 22dfdda..fee0261 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,13 @@ 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), - Rational(BigRational), + Int(NInt), + Rational(Box), Float(f64), Complex(Complex64), } @@ -37,7 +39,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), } @@ -53,29 +55,29 @@ 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 { fn from(x: BigRational) -> Self { - NNum::Rational(x) + 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 { @@ -83,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)), } } } @@ -122,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); @@ -133,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 @@ -176,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, } } @@ -215,7 +214,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()), } @@ -224,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) @@ -259,8 +258,8 @@ 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::Int(i) => Some(BigRational::from(i.to_bigint().into_owned())), + NNum::Rational(r) => Some(*r.clone()), NNum::Float(_) => None, NNum::Complex(_) => None, } @@ -277,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), @@ -325,8 +324,8 @@ 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::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), } @@ -339,7 +338,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)), } @@ -349,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)) } @@ -374,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(_) => { @@ -391,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(), @@ -443,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() { @@ -453,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, @@ -463,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 } @@ -480,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, } @@ -494,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), } } @@ -506,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())) @@ -520,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())) @@ -581,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) @@ -599,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); @@ -684,6 +688,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 +711,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.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)), } @@ -795,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 @@ -805,7 +821,7 @@ impl NNum { binary_match!( self, other, - Integer::mod_floor, + NInt::mod_floor, Rem::rem, f64::rem_euclid, Rem::rem @@ -825,7 +841,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 +864,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 +876,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), } @@ -877,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), } @@ -912,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), } @@ -962,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() { @@ -1043,27 +1028,31 @@ 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 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) => 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 d5c216c..62958be 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(), } } } @@ -516,7 +518,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 +590,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 +616,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())), }, @@ -634,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 7d241cd..9544063 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,10 +1,14 @@ extern crate noulith; -// use std::rc::Rc; use noulith::simple_eval; +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: i32) -> Obj { - Obj::i32(n) +fn i(n: i64) -> Obj { + Obj::i64(n) } #[test] @@ -27,6 +31,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)); @@ -108,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)); @@ -239,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] @@ -267,6 +298,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!( @@ -405,6 +447,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)); @@ -513,3 +596,13 @@ 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) + ); +}