ReviewOS

also looking at this

home-lang/home

fix(interpreter): match range and or patterns in match statements

#930
Open mkantautas wants to merge fix/interpreter-range-or-patterns into main
2 files +90 -0
packages/interpreter/src/interpreter.zigmodified+24-0
Changes to packages/interpreter/src/interpreter.zig
@@ -12392,6 +12392,30 @@ pub const Interpreter = struct {
1239212392
1239312393 return true;
1239412394 },
12395 .Range => |range_pattern| {
12396 // Range pattern: start..end (exclusive) or start..=end (inclusive)
12397 if (value != .Int) return false;
12398 const start = try self.evaluateExpression(range_pattern.start, env);
12399 const end = try self.evaluateExpression(range_pattern.end, env);
12400 if (start != .Int or end != .Int) return false;
12401 if (value.Int < start.Int) return false;
12402 return if (range_pattern.inclusive) value.Int <= end.Int else value.Int < end.Int;
12403 },
12404 .Or => |or_patterns| {
12405 // Or pattern: matches if any alternative matches. Each
12406 // alternative binds into a scratch scope so a failed one
12407 // leaves no bindings behind; the winner's are copied to env.
12408 for (or_patterns) |alt_pattern| {
12409 var alt_env = Environment.init(self.arena.allocator(), env);
12410 defer alt_env.deinit();
12411 if (try self.matchPatternNode(alt_pattern, value, &alt_env)) {
12412 var it = alt_env.bindings.iterator();
12413 while (it.next()) |entry| try env.define(entry.key_ptr.*, entry.value_ptr.*);
12414 return true;
12415 }
12416 }
12417 return false;
12418 },
1239512419 else => {
1239612420 // Other pattern types not yet implemented
1239712421 return false;
packages/interpreter/tests/program_eval_test.zigmodified+66-0
Changes to packages/interpreter/tests/program_eval_test.zig
@@ -75,6 +75,72 @@ test "eval: multiple statements and a returning function" {
7575 );
7676}
7777
78test "eval: match statement range and or patterns" {
79 try runProgram(
80 \\fn classify(x: int): int {
81 \\ match x {
82 \\ 0..=9 => return 1,
83 \\ 10..20 => return 2,
84 \\ 30 | 40 => return 3,
85 \\ 60 | 70..=75 => return 4,
86 \\ 50 => return 5,
87 \\ n if n > 100 => return n,
88 \\ _ => return 0,
89 \\ }
90 \\ return -1
91 \\}
92 \\
93 \\fn main() {
94 \\ assert(classify(0) == 1)
95 \\ assert(classify(9) == 1)
96 \\ assert(classify(10) == 2)
97 \\ assert(classify(19) == 2)
98 \\ assert(classify(20) == 0)
99 \\ assert(classify(30) == 3)
100 \\ assert(classify(40) == 3)
101 \\ assert(classify(41) == 0)
102 \\ assert(classify(60) == 4)
103 \\ assert(classify(75) == 4)
104 \\ assert(classify(76) == 0)
105 \\ assert(classify(50) == 5)
106 \\ assert(classify(101) == 101)
107 \\ assert(classify(-1) == 0)
108 \\}
109 );
110}
111
112test "eval: failed or-pattern alternatives leave no bindings" {
113 try runProgram(
114 \\struct P {
115 \\ a: int,
116 \\ b: int,
117 \\}
118 \\
119 \\fn after_failed_alternatives(p: P): int {
120 \\ let x = 99
121 \\ match p {
122 \\ P { a: x, b: 0 } | P { a: x, b: 3 } => return -1,
123 \\ _ => return x,
124 \\ }
125 \\ return -2
126 \\}
127 \\
128 \\fn from_matching_alternative(p: P): int {
129 \\ match p {
130 \\ P { a: y, b: 0 } | P { a: y, b: 2 } => return y,
131 \\ _ => return -1,
132 \\ }
133 \\ return -2
134 \\}
135 \\
136 \\fn main() {
137 \\ let p = P { a: 1, b: 2 }
138 \\ assert(after_failed_alternatives(p) == 99)
139 \\ assert(from_matching_alternative(p) == 1)
140 \\}
141 );
142}
143
78144test "eval: finite recursive calls remain valid" {
79145 try runProgram(
80146 \\fn countdown(n: i32): i32 {