diff --git a/changelog.md b/changelog.md index 91b6e2f7..1f4df0fe 100644 --- a/changelog.md +++ b/changelog.md @@ -25,7 +25,7 @@ This version is not yet released. If you are reading this on the website, then t - Add the experimental [`fft`](https://uiua.org/docs/fft) function, which performs the Fast Fourier transform - The inverse FFT is also supported via [`un °`](https://uiua.org/docs/un) - Add the experimental [`astar`](https://uiua.org/docs/astar) modifier, which performs the A* pathfinding algorithm -- Add the experimental [`but ⫯`](https://uiua.org/docs/but) modifier, which is a complement to [`on ⟜`](https://uiua.org/docs/on) and [`by ⊸`](https://uiua.org/docs/by) +- Add the experimental [`but ⤙`](https://uiua.org/docs/but) and [`with ⤚`](https://uiua.org/docs/with) modifiers, which are compliments to [`on ⟜`](https://uiua.org/docs/on) and [`by ⊸`](https://uiua.org/docs/by) - Non-scalar [`switch ⨬`](https://uiua.org/docs/switch) and [`repeat ⍥`](https://uiua.org/docs/repeat) now follow the same distribution and [`fix ¤`](https://uiua.org/docs/fix) rules as [`rows ≡`](https://uiua.org/docs/rows) - Add the `# Track caller!` semantic comment, which prevents stack traces from going below the function that contains it - Deprecate the experimental [`coordinate ⟔`](https://uiua.org/docs/coordinate) function, as it is seldom needed and easy to implement with other functions diff --git a/site/primitives.json b/site/primitives.json index 41350715..6746d880 100644 --- a/site/primitives.json +++ b/site/primitives.json @@ -385,6 +385,14 @@ "class": "DyadicPervasive", "description": "Take the arctangent of two numbers" }, + "backward": { + "glyph": "¨", + "outputs": 1, + "modifier_args": 1, + "class": "Stack", + "description": "Call a function with its arguments reversed", + "experimental": true + }, "bits": { "glyph": "⋯", "args": 1, @@ -415,7 +423,7 @@ "description": "Call two functions on two distinct sets of values" }, "but": { - "glyph": "⫯", + "glyph": "⤙", "outputs": 1, "modifier_args": 1, "class": "Stack", @@ -1269,6 +1277,14 @@ "class": "DyadicArray", "description": "The n-wise windows of an array" }, + "with": { + "glyph": "⤚", + "outputs": 1, + "modifier_args": 1, + "class": "Stack", + "description": "Call a function but keep its first argument under the outputs on the stack", + "experimental": true + }, "xlsx": { "args": 1, "outputs": 1, diff --git a/src/compile/modifier.rs b/src/compile/modifier.rs index e8f8df92..6ee40b0e 100644 --- a/src/compile/modifier.rs +++ b/src/compile/modifier.rs @@ -475,7 +475,7 @@ impl Compiler { }}; } match prim { - Dip | Gap | On | By | But => { + Dip | Gap | On | By | But | With => { // Compile operands let (mut instrs, sig) = self.compile_operand_word(modified.operands[0].clone())?; // Dip (|1 …) . diagnostic @@ -614,6 +614,38 @@ impl Compiler { instrs.push(Instr::Prim(Flip, span)); Signature::new(sig.args.max(1), sig.outputs + 1) } + With => { + instrs.insert( + 0, + Instr::CopyToTemp { + stack: TempStack::Inline, + count: 1, + span, + }, + ); + instrs.push(Instr::PopTemp { + stack: TempStack::Inline, + count: 1, + span, + }); + instrs.push(Instr::Prim(Flip, span)); + if sig.outputs >= 2 { + for _ in 0..sig.outputs - 1 { + instrs.push(Instr::PushTemp { + stack: TempStack::Inline, + count: 1, + span, + }); + instrs.push(Instr::Prim(Flip, span)); + } + instrs.push(Instr::PopTemp { + stack: TempStack::Inline, + count: sig.outputs - 1, + span, + }); + } + Signature::new(sig.args.max(1), sig.outputs + 1) + } _ => unreachable!(), }; if call { diff --git a/src/primitive/defs.rs b/src/primitive/defs.rs index 88343360..b5217491 100644 --- a/src/primitive/defs.rs +++ b/src/primitive/defs.rs @@ -1759,6 +1759,26 @@ primitive!( /// /// [on] is equivalent to [fork][identity], but can often be easier to read. ([1], On, Stack, ("on", '⟜')), + /// Call a function but keep its last argument on the top of the stack + /// + /// ex: # Experimental! + /// : [⤙+ 2 5] + /// : [⤙- 2 5] + /// [but] can be used to copy a value from deep in the stack, or to move it. + /// ex: # Experimental! + /// : [⤙⊙⊙⊙∘ 1 2 3 4] + /// : [⤙⊙⊙⊙◌ 1 2 3 4] + ([1], But, Stack, ("but", '⤙')), + /// Call a function but keep its first argument under the outputs on the stack + /// + /// ex: # Experimental! + /// : [⤚+ 2 5] + /// : [⤚- 2 5] + /// [with] can be used to copy a value from the top of the stack to a position deeper, or to move it. + /// ex: # Experimental! + /// : [⤚⊙⊙⊙∘ 1 2 3 4] + /// : [⤚⋅⊙⊙∘ 1 2 3 4] + ([1], With, Stack, ("with", '⤚')), /// Duplicate a function's last argument before calling it /// /// If you want to filter out every element of an array that is not [less than] 10, you can use [keep]. @@ -1776,17 +1796,6 @@ primitive!( /// : ⊜□⊸≠ @ "Hey there buddy" /// : ⊕□⊸◿ 5 [2 9 5 21 10 17 3 35] ([1], By, Stack, ("by", '⊸')), - /// Call a function but keep its last argument on the top of the stack - /// - /// ex: # Experimental! - /// : [⫯+ 2 5] - /// : [⫯- 2 5] - /// - /// [but] can be used to copy a value from deep in the stack, or to move it. - /// ex: # Experimental! - /// : [⫯⊙⊙⊙∘ 1 2 3 4] - /// : [⫯⊙⊙⊙◌ 1 2 3 4] - ([1], But, Stack, ("but", '⫯')), /// Call a function with its arguments reversed /// /// This is a modifier version of [flip]. diff --git a/src/primitive/mod.rs b/src/primitive/mod.rs index bef6dc17..13552db4 100644 --- a/src/primitive/mod.rs +++ b/src/primitive/mod.rs @@ -395,7 +395,7 @@ impl Primitive { use SysOp::*; matches!( self, - (But | Backward) + (But | With | Backward) | (Orient | Coordinate | Astar | Fft | Triangle | Case) | Sys(Ffi | MemCopy | MemFree | TlsListen) | (Stringify | Quote | Sig) @@ -847,10 +847,11 @@ impl Primitive { | Primitive::Sig | Primitive::Comptime | Primitive::Dip - | Primitive::On - | Primitive::By | Primitive::Gap + | Primitive::On | Primitive::But + | Primitive::With + | Primitive::By | Primitive::Backward | Primitive::Un | Primitive::Under diff --git a/tests/units.ua b/tests/units.ua index a4f025a5..2e208acc 100644 --- a/tests/units.ua +++ b/tests/units.ua @@ -620,10 +620,15 @@ repr[{[[1 2][2 3]][3 4]}{[[1 2][2 3]]□{[[1 2][2 3]]□□[3 4]}}] ⍤⟜≍: [1 3 2] °(°[⊙⊙∘]⊏0_2_1) 1 2 3 # But -⍤⟜≍: [3 ¯3] [⫯¯ 3] -⍤⟜≍: [5 8] [⫯+ 3 5] -⍤⟜≍: [4 1 2 3 4] [⫯⊙⊙⊙∘ 1 2 3 4] -⍤⟜≍: [4 1 2 3] [⫯⊙⊙⊙◌ 1 2 3 4] +⍤⟜≍: [3 ¯3] [⤙¯ 3] +⍤⟜≍: [5 8] [⤙+ 3 5] +⍤⟜≍: [4 1 2 3 4] [⤙⊙⊙⊙∘ 1 2 3 4] +⍤⟜≍: [4 1 2 3] [⤙⊙⊙⊙◌ 1 2 3 4] + +⍤⟜≍: [¯3 3] [⤚¯ 3] +⍤⟜≍: [8 3] [⤚+ 3 5] +⍤⟜≍: [1 2 3 4 1] [⤚⊙⊙⊙∘ 1 2 3 4] +⍤⟜≍: [2 3 4 1] [⤚⋅⊙⊙∘ 1 2 3 4] # Orient ⍤⟜≍: °⍉⟜(⮌¯1) °△ 2_3_4