diff --git a/harness/test/012_arith.eu b/harness/test/012_arith.eu index 0c34d21..248d916 100644 --- a/harness/test/012_arith.eu +++ b/harness/test/012_arith.eu @@ -5,6 +5,7 @@ (l - r): __SUB(l, r) (l * r): __MUL(l, r) (l / r): __DIV(l, r) +(l % r): __MOD(l, r) (l > r): __GT(l, r) (l < r): __LT(l, r) @@ -30,14 +31,15 @@ integer-arithmetic: { f: 1 >= 2 } - vals: { - four: 2 + 2 - also-four: 2 * 2 - three: 5 - 2 - twelve: 24 / 2 + true-calcs: { + four: 2 + 2 //= 4 + also-four: 2 * 2 //= 4 + three: 5 - 2 //= 3 + twelve: 24 / 2 //= 12 + two: 5 % 3 //= 2 } - pass: (trues values all-true?) ∧ (falses values map(not) all-true?) + pass: (trues values all-true?) ∧ (falses values map(not) all-true?) ∧ (true-calcs values all-true?) } floating-point-arithmetic: { @@ -60,10 +62,11 @@ floating-point-arithmetic: { } vals: { - fourish: 2.01 + 2.01 - also-fourish: 2.01 * 2.01 - threeish: 5.01 - 2.01 - twelveish: 24.01 / 2.01 + fourish: (2.01 + 2.01) - 4 < 0.1 + also-fourish: (2.01 * 2.01) - 4 < 0.1 + threeish: (5.01 - 2.01) - 3 < 0.1 + twelveish: (24.01 / 2.01) - 12 < 0.1 + twoish: (5.01 % 3) - 2 < 0.1 } pass: (trues values all-true?) ∧ (falses values map(not) all-true?) @@ -107,10 +110,11 @@ mixed-arithmetic: { } vals: { - fourish: 2.01 + 2 - also-fourish: 2.01 * 2 - threeish: 5.01 - 2 - twelveish: 24.01 / 2 + fourish: (2.01 + 2) - 4 < 0.1 + also-fourish: (2.01 * 2) - 4 < 0.1 + threeish: (5.01 - 2) - 3 < 0.1 + twelveish: (24.01 / 2) - 12 < 0.1 + twoish: (5.01 % 3) - 2 < 0.1 } pass: ((trues values) ++ (trues2 values) all-true?) ∧ diff --git a/harness/test/036_takes_and_drops.eu b/harness/test/036_takes_and_drops.eu index 6cee3c1..35d11bc 100644 --- a/harness/test/036_takes_and_drops.eu +++ b/harness/test/036_takes_and_drops.eu @@ -10,6 +10,13 @@ tests: { ι: [0, 1, 2, 3] take-until(>= 0) //= [] κ: [0, 1] cycle take(5) //= [0, 1, 0, 1, 0] λ: [] cycle take(10) //= [] + μ: ints-from(0) split-at(4) first //= [0, 1, 2, 3] + ν: range(0, 10) split-at(5) //= [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]] + ξ: ints-from(0) take-while(_ < 5) //= [0, 1, 2, 3, 4] + ο: range(0, 8) split-after(_ < 5) //= [[0, 1, 2, 3, 4], [5, 6, 7]] + π: range(0, 8) split-when(_ > 5) //= [[0, 1, 2, 3, 4, 5], [6, 7]] + ρ: ints-from(0) split-after(_ < 5) first //= [0, 1, 2, 3, 4] + σ: ints-from(0) split-after(_ < 5) second take(2) //= [5, 6] } RESULT: tests values all-true? then(:PASS, :FAIL) diff --git a/lib/prelude.eu b/lib/prelude.eu index 403ef01..b18d437 100644 --- a/lib/prelude.eu +++ b/lib/prelude.eu @@ -526,8 +526,15 @@ take(n, l): __IF((n zero?) ∨ (l nil?), [], cons(l head, take(n dec, l tail))) ` "`drop(n, l)` - return result of dropping integer `n` elements from list `l`." drop(n, l): __IF((n zero?), l, drop(n dec, l tail)) +` "'split-at(n, l) - split list in to at `n`th item and return pair." +split-at(n, l): { + aux(n, xs, prefix): if((xs nil?) ∨ (n zero?), [prefix reverse, xs], aux(n dec, xs tail, cons(xs head, prefix))) +}.aux(n, l, []) + ` "`take-while(p?, l)` - initial elements of list `l` while `p?` is true." -take-while(p?, l): if(l nil?, [], if(l head p?, cons(l head, take-while(p?, l tail)), take-while(p?, l tail))) +take-while(p?, l): { + aux(xs, prefix): if(not(xs nil?) ∧ (xs head p?), aux(xs tail, cons(xs head, prefix)), prefix reverse) +}.aux(l, []) ` "`take-until(p?, l)` - initial elements of list `l` while `p?` is false." take-until(p?): take-while(p? complement) @@ -538,6 +545,16 @@ drop-while(p?, l): if(l nil?, [], if(l head p?, drop-while(p?, l tail), l)) ` "`drop-until(p?, l)` - skip initial elements of list `l` while `p?` is false." drop-until(p?): drop-while(p? complement) +` "`split-after(p?, l) - split list where `p?` becomes false and return pair." +split-after(p?, l): { + aux(xs, prefix): if((xs nil?) ∨ (xs head p?), + aux(xs tail, cons(xs head, prefix)), + [prefix reverse, xs]) +}.aux(l, []) + +` "`split-when(p?, l) - split list where `p?` becomes true and return pair." +split-when(p?, l): split-after(p? complement, l) + ` "`nth(n, l)` - return `n`th item of list if it exists, otherwise panic." nth(n, l): l drop(n) head diff --git a/src/core/desugar/ast.rs b/src/core/desugar/ast.rs index b7a7941..f8a6702 100644 --- a/src/core/desugar/ast.rs +++ b/src/core/desugar/ast.rs @@ -77,7 +77,7 @@ fn declaration_to_binding( if let Some(target) = &metadata.target { desugarer.record_target( target.to_string(), - metadata.doc.unwrap_or_else(|| "".to_string()), + metadata.doc.unwrap_or_default(), metadata.format, metadata.validations.unwrap_or_default(), ); diff --git a/src/core/export/embed.rs b/src/core/export/embed.rs index 9727f32..3b0dd5a 100644 --- a/src/core/export/embed.rs +++ b/src/core/export/embed.rs @@ -60,14 +60,14 @@ impl Embed for CoreExpr { Expr::Lookup(_, e, n, fb) => { elements.push(lit(sym("c-lookup"))); elements.push(e.embed()); - elements.push(lit(str(&n))); + elements.push(lit(str(n))); if let Some(x) = fb { elements.push(x.embed()); } } Expr::Name(_, n) => { elements.push(lit(sym("c-name"))); - elements.push(lit(str(&n))); + elements.push(lit(str(n))); } Expr::BlockAnaphor(_, anaphor) => { elements.push(lit(sym("c-bk-ana"))); @@ -151,11 +151,11 @@ impl Embed for CoreExpr { } Expr::ErrUnresolved(_, x) => { elements.push(lit(sym("e-unresolved"))); - elements.push(lit(str(&x))); + elements.push(lit(str(x))); } Expr::ErrRedeclaration(_, x) => { elements.push(lit(sym("e-redeclaration"))); - elements.push(lit(str(&x))); + elements.push(lit(str(x))); } Expr::ErrEliminated => { elements.push(lit(sym("e-eliminated"))); @@ -178,7 +178,7 @@ impl Embed for CoreExpr { impl Embed for Primitive { fn embed(&self) -> Expression { match self { - Primitive::Str(s) => lit(str(&s)), + Primitive::Str(s) => lit(str(s)), Primitive::Sym(s) => lit(sym(s)), Primitive::Num(n) => lit(num(n.clone())), Primitive::Bool(b) => list(vec![ diff --git a/src/core/mod.rs b/src/core/mod.rs index 2b5912d..2720daa 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,5 @@ //! The core expression representation and various processing phases +#![allow(clippy::result_large_err)] pub mod analyse; pub mod anaphora; pub mod cook; diff --git a/src/eval/machine/env.rs b/src/eval/machine/env.rs index e9b24f1..a74d2ce 100644 --- a/src/eval/machine/env.rs +++ b/src/eval/machine/env.rs @@ -275,7 +275,7 @@ where if smid != Smid::default() { trace.push(smid); } - match (*frame).next { + match frame.next { Some(f) => frame = ScopedPtr::from_non_null(guard, f), None => return trace, } @@ -290,7 +290,7 @@ where fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.bindings.len(); - match (*self).next { + match self.next { None => { if len > 0 { write!(f, "[×{}]→•", len) diff --git a/src/eval/stg/arith.rs b/src/eval/stg/arith.rs index e000293..e5eab60 100644 --- a/src/eval/stg/arith.rs +++ b/src/eval/stg/arith.rs @@ -179,6 +179,48 @@ impl StgIntrinsic for Div { impl CallGlobal2 for Div {} +/// MOD(l, r) - mod r from l +pub struct Mod; + +impl StgIntrinsic for Mod { + fn name(&self) -> &str { + "MOD" + } + + fn execute<'guard>( + &self, + machine: &mut dyn IntrinsicMachine, + view: MutatorHeapView<'guard>, + _emitter: &mut dyn Emitter, + args: &[Ref], + ) -> Result<(), crate::eval::error::ExecutionError> { + let x = num_arg(machine, view, &args[0])?; + let y = num_arg(machine, view, &args[1])?; + + if let (Some(l), Some(r)) = (x.as_i64(), y.as_i64()) { + let product = l + .checked_rem(r) + .map_or(Err(ExecutionError::NumericRangeError(x, y)), Ok)?; + machine_return_num(machine, view, Number::from(product)) + } else if let (Some(l), Some(r)) = (x.as_u64(), y.as_u64()) { + let product = l + .checked_rem(r) + .map_or(Err(ExecutionError::NumericRangeError(x, y)), Ok)?; + machine_return_num(machine, view, Number::from(product)) + } else if let (Some(l), Some(r)) = (x.as_f64(), y.as_f64()) { + if let Some(ret) = Number::from_f64(l % r) { + machine_return_num(machine, view, ret) + } else { + Err(ExecutionError::NumericDomainError(x, y)) + } + } else { + Err(ExecutionError::NumericDomainError(x, y)) + } + } +} + +impl CallGlobal2 for Mod {} + /// GT(l, r) l > r pub struct Gt; diff --git a/src/eval/stg/compiler.rs b/src/eval/stg/compiler.rs index cbd43aa..068ce6b 100644 --- a/src/eval/stg/compiler.rs +++ b/src/eval/stg/compiler.rs @@ -1043,7 +1043,7 @@ impl<'rt> Compiler<'rt> { let mut index = KEmptyList.gref(); // binder.add(dsl::nil())?; // TODO: to CAF for (k, v) in block_map.iter().rev() { let v_index = self.compile_binding(binder, v.clone(), smid, false)?; - let kv_index = binder.add(dsl::pair(&k, v_index))?; + let kv_index = binder.add(dsl::pair(k, v_index))?; index = binder.add(dsl::cons(kv_index, index))?; } Ok(Holder::new(dsl::block(index))) diff --git a/src/eval/stg/mod.rs b/src/eval/stg/mod.rs index f0279b0..a45834e 100644 --- a/src/eval/stg/mod.rs +++ b/src/eval/stg/mod.rs @@ -43,6 +43,7 @@ pub fn make_standard_runtime(source_map: &mut SourceMap) -> Box Tag { - let set = RegexSet::new(&[ + let set = RegexSet::new([ r"^[nN]ull$", r"^NULL$", r"^~$", diff --git a/src/syntax/export/embed.rs b/src/syntax/export/embed.rs index 193388f..196236a 100644 --- a/src/syntax/export/embed.rs +++ b/src/syntax/export/embed.rs @@ -11,7 +11,7 @@ impl Embed for Literal { fn embed(&self) -> Expression { match self { Literal::Sym(_, s) => lit(sym(s)), - Literal::Str(_, s) => lit(str(&s)), + Literal::Str(_, s) => lit(str(s)), Literal::Num(_, n) => lit(num(n.clone())), } } diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs index 97c379d..e982fec 100644 --- a/src/syntax/lexer.rs +++ b/src/syntax/lexer.rs @@ -233,7 +233,7 @@ where /// consume a normal identifer fn normal(&mut self, i: ByteIndex) -> (ByteIndex, Token<'text>, ByteIndex) { - let e = self.consume(&is_normal_continuation); + let e = self.consume(is_normal_continuation); (i, Token::NormalIdentifier(self.slice(i, e)), e) } @@ -253,7 +253,7 @@ where /// consume an operator identifer fn oper(&mut self, i: ByteIndex) -> (ByteIndex, Token<'text>, ByteIndex) { - let e = self.consume(&is_oper_continuation); + let e = self.consume(is_oper_continuation); (i, Token::OperatorIdentifier(self.slice(i, e)), e) } @@ -278,7 +278,7 @@ where if is_normal_start(c) { if let Some((b, _)) = self.bump() { - let e = self.consume(&is_normal_continuation); + let e = self.consume(is_normal_continuation); return (i, Token::Symbol(self.slice(b, e)), e); } else { panic!("peek and next disagree");