Skip to content

Commit 0419a1b

Browse files
authored
Merge pull request #8431 from roc-lang/fold-rev
Add List.fold_rev
2 parents 188634a + 4037e8a commit 0419a1b

File tree

8 files changed

+89
-44
lines changed

8 files changed

+89
-44
lines changed

.github/workflows/ci_zig.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ jobs:
196196
run: |
197197
sudo snap install valgrind --classic
198198
valgrind --version
199-
./ci/valgind_clean.sh --leak-check=full --error-exitcode=1 --errors-for-leak-kinds=definite,possible ./zig-out/bin/snapshot --debug
199+
./ci/valgind_clean.sh --leak-check=full --error-exitcode=1 --errors-for-leak-kinds=definite,possible ./zig-out/bin/snapshot --debug --verbose
200200
201201
- name: check if statically linked (ubuntu)
202202
if: startsWith(matrix.os, 'ubuntu')

src/build/builtin_compiler/main.zig

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,12 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
337337
}
338338

339339
// Iterate through all defs and replace matching anno-only defs with low-level implementations
340-
const all_defs = env.store.sliceDefs(env.all_defs);
341-
for (all_defs) |def_idx| {
340+
const all_defs_slice = env.store.sliceDefs(env.all_defs);
341+
var def_indices = std.ArrayList(CIR.Def.Idx).empty;
342+
defer def_indices.deinit(gpa);
343+
try def_indices.appendSlice(gpa, all_defs_slice);
344+
345+
for (def_indices.items) |def_idx| {
342346
const def = env.store.getDef(def_idx);
343347
const expr = env.store.getExpr(def.expr);
344348

@@ -371,7 +375,7 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
371375
const arg_pattern_idx = try env.addPattern(.{ .assign = .{ .ident = arg_ident } }, base.Region.zero());
372376
try env.store.scratch.?.patterns.append(arg_pattern_idx);
373377
}
374-
const args_span = CIR.Pattern.Span{ .span = .{ .start = @intCast(patterns_start), .len = num_params } };
378+
const args_span = try env.store.patternSpanFrom(patterns_start);
375379

376380
// Create an e_runtime_error body that crashes when the function is called
377381
const error_msg_lit = try env.insertString("Low-level builtin not yet implemented in interpreter");
@@ -827,32 +831,7 @@ fn compileModule(
827831
defer new_def_indices.deinit(gpa);
828832

829833
if (new_def_indices.items.len > 0) {
830-
// Rebuild all_defs span to include both old and new defs
831-
// First, get the old def indices from extra_data
832-
const old_span = module_env.all_defs.span;
833-
const old_def_count = old_span.len;
834-
835-
// Allocate new space in extra_data for all defs (old + new)
836-
const new_span_start: u32 = @intCast(module_env.store.extra_data.len());
837-
838-
// Copy old def indices
839-
var i: u32 = 0;
840-
while (i < old_def_count) : (i += 1) {
841-
const idx = @as(collections.SafeList(u32).Idx, @enumFromInt(old_span.start + i));
842-
const old_def_idx = module_env.store.extra_data.get(idx).*;
843-
_ = try module_env.store.extra_data.append(gpa, old_def_idx);
844-
}
845-
846-
// Append new def indices
847-
for (new_def_indices.items) |new_def_idx| {
848-
_ = try module_env.store.extra_data.append(gpa, @intFromEnum(new_def_idx));
849-
}
850-
851-
// Update all_defs to point to the new span
852-
module_env.all_defs.span.start = new_span_start;
853-
module_env.all_defs.span.len = old_def_count + @as(u32, @intCast(new_def_indices.items.len));
854-
855-
// Rebuild the dependency graph and evaluation order to include new defs
834+
// Rebuild the dependency graph and evaluation order to include the updated defs
856835
const DependencyGraph = @import("can").DependencyGraph;
857836
var graph = try DependencyGraph.buildDependencyGraph(
858837
module_env,

src/build/roc/Builtin.roc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ Builtin :: [].{
7070

7171
$state
7272
}
73+
74+
fold_rev : List(item), state, (item, state -> state) -> state
75+
fold_rev = |list, init, step| {
76+
var $state = init
77+
var $index = list.len()
78+
79+
while $index > 0 {
80+
$index = $index - 1
81+
item = list_get_unsafe(list, $index)
82+
$state = step(item, $state)
83+
}
84+
85+
$state
86+
}
7387
}
7488

7589
Bool := [False, True].{

src/builtins/dec.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,20 @@ pub const RocDec = extern struct {
663663
pub fn atan(self: RocDec) RocDec {
664664
return fromF64(math.atan(self.toF64())).?;
665665
}
666+
667+
pub fn rem(
668+
self: RocDec,
669+
other: RocDec,
670+
roc_ops: *RocOps,
671+
) RocDec {
672+
// (n % 0) is an error
673+
if (other.num == 0) {
674+
roc_ops.crash("Decimal remainder by 0!");
675+
}
676+
677+
// For Dec, remainder is straightforward since both operands have the same scaling factor
678+
return RocDec{ .num = @rem(self.num, other.num) };
679+
}
666680
};
667681

668682
// A number has `k` trailing zeros if `10^k` divides into it cleanly

src/eval/interpreter.zig

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3577,7 +3577,7 @@ pub const Interpreter = struct {
35773577
.int => |l| try out.setInt(l + rhs.int),
35783578
.f32 => |l| out.setF32(l + rhs.f32),
35793579
.f64 => |l| out.setF64(l + rhs.f64),
3580-
.dec => |l| out.setDec(RocDec{ .num = l.num + rhs.dec.num }),
3580+
.dec => |l| out.setDec(RocDec.add(l, rhs.dec, roc_ops)),
35813581
}
35823582
out.is_initialized = true;
35833583
return out;
@@ -3595,7 +3595,7 @@ pub const Interpreter = struct {
35953595
.int => |l| try out.setInt(l - rhs.int),
35963596
.f32 => |l| out.setF32(l - rhs.f32),
35973597
.f64 => |l| out.setF64(l - rhs.f64),
3598-
.dec => |l| out.setDec(RocDec{ .num = l.num - rhs.dec.num }),
3598+
.dec => |l| out.setDec(RocDec.sub(l, rhs.dec, roc_ops)),
35993599
}
36003600
out.is_initialized = true;
36013601
return out;
@@ -3613,7 +3613,7 @@ pub const Interpreter = struct {
36133613
.int => |l| try out.setInt(l * rhs.int),
36143614
.f32 => |l| out.setF32(l * rhs.f32),
36153615
.f64 => |l| out.setF64(l * rhs.f64),
3616-
.dec => |l| out.setDec(RocDec{ .num = @divTrunc(l.num * rhs.dec.num, RocDec.one_point_zero_i128) }),
3616+
.dec => |l| out.setDec(RocDec.mul(l, rhs.dec, roc_ops)),
36173617
}
36183618
out.is_initialized = true;
36193619
return out;
@@ -3642,8 +3642,7 @@ pub const Interpreter = struct {
36423642
},
36433643
.dec => |l| {
36443644
if (rhs.dec.num == 0) return error.DivisionByZero;
3645-
const scaled_lhs = l.num * RocDec.one_point_zero_i128;
3646-
out.setDec(RocDec{ .num = @divTrunc(scaled_lhs, rhs.dec.num) });
3645+
out.setDec(RocDec.div(l, rhs.dec, roc_ops));
36473646
},
36483647
}
36493648
out.is_initialized = true;
@@ -3672,9 +3671,9 @@ pub const Interpreter = struct {
36723671
out.setF64(@trunc(l / rhs.f64));
36733672
},
36743673
.dec => |l| {
3674+
// For Dec, div and div_trunc are the same since it's already integer-like
36753675
if (rhs.dec.num == 0) return error.DivisionByZero;
3676-
const scaled_lhs = l.num * RocDec.one_point_zero_i128;
3677-
out.setDec(RocDec{ .num = @divTrunc(scaled_lhs, rhs.dec.num) });
3676+
out.setDec(RocDec.div(l, rhs.dec, roc_ops));
36783677
},
36793678
}
36803679
out.is_initialized = true;
@@ -3704,7 +3703,7 @@ pub const Interpreter = struct {
37043703
},
37053704
.dec => |l| {
37063705
if (rhs.dec.num == 0) return error.DivisionByZero;
3707-
out.setDec(RocDec{ .num = @rem(l.num, rhs.dec.num) });
3706+
out.setDec(RocDec.rem(l, rhs.dec, roc_ops));
37083707
},
37093708
}
37103709
out.is_initialized = true;
@@ -4717,22 +4716,22 @@ pub const Interpreter = struct {
47174716
result_layout: Layout,
47184717
lhs: StackValue,
47194718
rhs: StackValue,
4719+
roc_ops: *RocOps,
47204720
) !StackValue {
47214721
const lhs_dec = try self.stackValueToDecimal(lhs);
47224722
const rhs_dec = try self.stackValueToDecimal(rhs);
47234723

47244724
const result_dec: RocDec = switch (op) {
4725-
.add => RocDec{ .num = lhs_dec.num + rhs_dec.num },
4726-
.sub => RocDec{ .num = lhs_dec.num - rhs_dec.num },
4727-
.mul => RocDec{ .num = @divTrunc(lhs_dec.num * rhs_dec.num, RocDec.one_point_zero_i128) },
4725+
.add => RocDec.add(lhs_dec, rhs_dec, roc_ops),
4726+
.sub => RocDec.sub(lhs_dec, rhs_dec, roc_ops),
4727+
.mul => RocDec.mul(lhs_dec, rhs_dec, roc_ops),
47284728
.div, .div_trunc => blk: {
47294729
if (rhs_dec.num == 0) return error.DivisionByZero;
4730-
const scaled_lhs = lhs_dec.num * RocDec.one_point_zero_i128;
4731-
break :blk RocDec{ .num = @divTrunc(scaled_lhs, rhs_dec.num) };
4730+
break :blk RocDec.div(lhs_dec, rhs_dec, roc_ops);
47324731
},
47334732
.rem => blk: {
47344733
if (rhs_dec.num == 0) return error.DivisionByZero;
4735-
break :blk RocDec{ .num = @rem(lhs_dec.num, rhs_dec.num) };
4734+
break :blk RocDec.rem(lhs_dec, rhs_dec, roc_ops);
47364735
},
47374736
else => @panic("evalDecBinop: unhandled decimal operation"),
47384737
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# META
2+
~~~ini
3+
description=Basic List.fold_rev - demonstrates right-to-left iteration
4+
type=repl
5+
~~~
6+
# SOURCE
7+
~~~roc
8+
» List.fold_rev([1, 2, 3], 0, |x, acc| acc * 10 + x)
9+
~~~
10+
# OUTPUT
11+
321
12+
# PROBLEMS
13+
NIL
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# META
2+
~~~ini
3+
description=List.fold_rev with empty list
4+
type=repl
5+
~~~
6+
# SOURCE
7+
~~~roc
8+
» List.fold_rev([], 42, |x, acc| x + acc)
9+
~~~
10+
# OUTPUT
11+
42
12+
# PROBLEMS
13+
NIL
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# META
2+
~~~ini
3+
description=List.fold_rev with subtraction (order-dependent)
4+
type=repl
5+
~~~
6+
# SOURCE
7+
~~~roc
8+
» List.fold_rev([10, 11], 54, |x, acc| x - acc)
9+
~~~
10+
# OUTPUT
11+
53
12+
# PROBLEMS
13+
NIL

0 commit comments

Comments
 (0)