diff --git a/src/eval/interpreter.zig b/src/eval/interpreter.zig index f3b49ba50b..ba627a8b0a 100644 --- a/src/eval/interpreter.zig +++ b/src/eval/interpreter.zig @@ -509,6 +509,77 @@ pub const Interpreter = struct { .s_expr => |sx| { _ = try self.evalExprMinimal(sx.expr, roc_ops, null); }, + .s_for => |for_stmt| { + // Evaluate the list expression + const expr_ct_var = can.ModuleEnv.varFrom(for_stmt.expr); + const expr_rt_var = try self.translateTypeVar(self.env, expr_ct_var); + const list_value = try self.evalExprMinimal(for_stmt.expr, roc_ops, expr_rt_var); + defer list_value.decref(&self.runtime_layout_store, roc_ops); + + // Get the list layout + if (list_value.layout.tag != .list) { + return error.TypeMismatch; + } + const elem_layout_idx = list_value.layout.data.list; + const elem_layout = self.runtime_layout_store.getLayout(elem_layout_idx); + const elem_size: usize = @intCast(self.runtime_layout_store.layoutSize(elem_layout)); + + // Get the RocList header + const list_header: *const RocList = @ptrCast(@alignCast(list_value.ptr.?)); + const list_len = list_header.len(); + + // Get the element type for binding + const patt_ct_var = can.ModuleEnv.varFrom(for_stmt.patt); + const patt_rt_var = try self.translateTypeVar(self.env, patt_ct_var); + + // Iterate over each element + var i: usize = 0; + while (i < list_len) : (i += 1) { + // Get pointer to element + const elem_ptr = if (list_header.bytes) |buffer| + buffer + i * elem_size + else + return error.TypeMismatch; + + // Create a StackValue from the element + var elem_value = StackValue{ + .ptr = elem_ptr, + .layout = elem_layout, + .is_initialized = true, + }; + + // Increment refcount since we're creating a new reference + elem_value.incref(); + + // Bind the pattern to the element value + var temp_binds = try std.array_list.AlignedManaged(Binding, null).initCapacity(self.allocator, 4); + defer { + self.trimBindingList(&temp_binds, 0, roc_ops); + temp_binds.deinit(); + } + + if (!try self.patternMatchesBind(for_stmt.patt, elem_value, patt_rt_var, roc_ops, &temp_binds)) { + elem_value.decref(&self.runtime_layout_store, roc_ops); + return error.TypeMismatch; + } + + // Add bindings to the environment + const loop_bindings_start = self.bindings.items.len; + for (temp_binds.items) |binding| { + try self.bindings.append(binding); + } + + // Evaluate the body + const body_result = try self.evalExprMinimal(for_stmt.body, roc_ops, null); + body_result.decref(&self.runtime_layout_store, roc_ops); + + // Clean up bindings for this iteration + self.trimBindingList(&self.bindings, loop_bindings_start, roc_ops); + + // Decrement the element reference (it was incremented above) + elem_value.decref(&self.runtime_layout_store, roc_ops); + } + }, else => return error.NotImplemented, } } diff --git a/test/snapshots/repl/for_loop_complex_mutation.md b/test/snapshots/repl/for_loop_complex_mutation.md new file mode 100644 index 0000000000..a158a2fdbd --- /dev/null +++ b/test/snapshots/repl/for_loop_complex_mutation.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop with complex var mutation +type=repl +~~~ +# SOURCE +~~~roc +» countEvens = { var count_ = 0; var sum_ = 0; for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] { if n % 2 == 0 { count_ = count_ + 1; sum_ = sum_ + n } else { {} } }; count_ * sum_ } +~~~ +# OUTPUT +assigned `countEvens` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_empty_list.md b/test/snapshots/repl/for_loop_empty_list.md new file mode 100644 index 0000000000..059d84cd25 --- /dev/null +++ b/test/snapshots/repl/for_loop_empty_list.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop with empty list +type=repl +~~~ +# SOURCE +~~~roc +» unchanged = { var value_ = 42; for n in [] { value_ = n }; value_ } +~~~ +# OUTPUT +assigned `unchanged` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_list_bool.md b/test/snapshots/repl/for_loop_list_bool.md new file mode 100644 index 0000000000..4ac96835be --- /dev/null +++ b/test/snapshots/repl/for_loop_list_bool.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop iterating over List Bool +type=repl +~~~ +# SOURCE +~~~roc +» result = { var allTrue_ = Bool.True; for b in [Bool.True, Bool.True, Bool.False] { if b == Bool.False { allTrue_ = Bool.False } else { {} } }; allTrue_ } +~~~ +# OUTPUT +assigned `result` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_list_str.md b/test/snapshots/repl/for_loop_list_str.md new file mode 100644 index 0000000000..00cda7fac9 --- /dev/null +++ b/test/snapshots/repl/for_loop_list_str.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop iterating over List Str +type=repl +~~~ +# SOURCE +~~~roc +» count = { var counter_ = 0; for _ in ["hello", "world", "test"] { counter_ = counter_ + 1 }; counter_ } +~~~ +# OUTPUT +assigned `count` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_list_u64.md b/test/snapshots/repl/for_loop_list_u64.md new file mode 100644 index 0000000000..54ff818254 --- /dev/null +++ b/test/snapshots/repl/for_loop_list_u64.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop iterating over List U64 +type=repl +~~~ +# SOURCE +~~~roc +» sum = { var total_ = 0; for n in [1, 2, 3, 4, 5] { total_ = total_ + n }; total_ } +~~~ +# OUTPUT +assigned `sum` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_nested.md b/test/snapshots/repl/for_loop_nested.md new file mode 100644 index 0000000000..eb3f29cff7 --- /dev/null +++ b/test/snapshots/repl/for_loop_nested.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=Nested for loops +type=repl +~~~ +# SOURCE +~~~roc +» product = { var result_ = 0; for i in [1, 2, 3] { for j in [10, 20] { result_ = result_ + (i * j) } }; result_ } +~~~ +# OUTPUT +assigned `product` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_var_conditional_persist.md b/test/snapshots/repl/for_loop_var_conditional_persist.md new file mode 100644 index 0000000000..774ec6ac6e --- /dev/null +++ b/test/snapshots/repl/for_loop_var_conditional_persist.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop with var that persists across iterations with conditional updates +type=repl +~~~ +# SOURCE +~~~roc +» result = { var lastEven_ = 0; var evenCount_ = 0; for n in [1, 2, 3, 4, 5, 6, 7, 8] { if n % 2 == 0 { lastEven_ = n; evenCount_ = evenCount_ + 1 } else { {} } }; lastEven_ * evenCount_ } +~~~ +# OUTPUT +assigned `result` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_var_every_iteration.md b/test/snapshots/repl/for_loop_var_every_iteration.md new file mode 100644 index 0000000000..619d979cd0 --- /dev/null +++ b/test/snapshots/repl/for_loop_var_every_iteration.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop with var reassignment on every iteration +type=repl +~~~ +# SOURCE +~~~roc +» result = { var prev_ = 0; var count_ = 0; for n in [10, 20, 30, 40, 50] { count_ = count_ + 1; prev_ = n }; prev_ + count_ } +~~~ +# OUTPUT +assigned `result` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_var_reassign_tracking.md b/test/snapshots/repl/for_loop_var_reassign_tracking.md new file mode 100644 index 0000000000..c1aa8ca635 --- /dev/null +++ b/test/snapshots/repl/for_loop_var_reassign_tracking.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop with var reassignment tracking across iterations +type=repl +~~~ +# SOURCE +~~~roc +» result = { var sum_ = 0; var max_ = 0; for n in [3, 7, 2, 9, 1] { sum_ = sum_ + n; if n > max_ { max_ = n } else { {} } }; sum_ + max_ } +~~~ +# OUTPUT +assigned `result` +# PROBLEMS +NIL diff --git a/test/snapshots/repl/for_loop_with_var.md b/test/snapshots/repl/for_loop_with_var.md new file mode 100644 index 0000000000..b452fcf16a --- /dev/null +++ b/test/snapshots/repl/for_loop_with_var.md @@ -0,0 +1,13 @@ +# META +~~~ini +description=For loop with var reassignment +type=repl +~~~ +# SOURCE +~~~roc +» result = { var prev_ = 0; var count_ = 0; for n in [10, 20, 30, 40, 50] { count_ = count_ + 1; prev_ = n }; prev_ + count_ } +~~~ +# OUTPUT +assigned `result` +# PROBLEMS +NIL diff --git a/test/snapshots/statement/for_loop_complex_mutation.md b/test/snapshots/statement/for_loop_complex_mutation.md new file mode 100644 index 0000000000..6eb925f71f --- /dev/null +++ b/test/snapshots/statement/for_loop_complex_mutation.md @@ -0,0 +1,185 @@ +# META +~~~ini +description=For loop with complex var mutation +type=snippet +~~~ +# SOURCE +~~~roc +countEvens : U64 +countEvens = { + var count_ = 0 + var sum_ = 0 + for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] { + if n % 2 == 0 { + count_ = count_ + 1 + sum_ = sum_ + n + } else { + {} + } + } + count_ * sum_ +} + +expect countEvens == 150 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,CloseSquare,OpenCurly, +KwIf,LowerIdent,OpPercent,Int,OpEquals,Int,OpenCurly, +LowerIdent,OpAssign,LowerIdent,OpPlus,Int, +LowerIdent,OpAssign,LowerIdent,OpPlus,LowerIdent, +CloseCurly,KwElse,OpenCurly, +OpenCurly,CloseCurly, +CloseCurly, +CloseCurly, +LowerIdent,OpStar,LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "countEvens") + (ty (name "U64"))) + (s-decl + (p-ident (raw "countEvens")) + (e-block + (statements + (s-var (name "count_") + (e-int (raw "0"))) + (s-var (name "sum_") + (e-int (raw "0"))) + (s-for + (p-ident (raw "n")) + (e-list + (e-int (raw "1")) + (e-int (raw "2")) + (e-int (raw "3")) + (e-int (raw "4")) + (e-int (raw "5")) + (e-int (raw "6")) + (e-int (raw "7")) + (e-int (raw "8")) + (e-int (raw "9")) + (e-int (raw "10"))) + (e-block + (statements + (e-if-then-else + (e-binop (op "==") + (e-binop (op "%") + (e-ident (raw "n")) + (e-int (raw "2"))) + (e-int (raw "0"))) + (e-block + (statements + (s-decl + (p-ident (raw "count_")) + (e-binop (op "+") + (e-ident (raw "count_")) + (e-int (raw "1")))) + (s-decl + (p-ident (raw "sum_")) + (e-binop (op "+") + (e-ident (raw "sum_")) + (e-ident (raw "n")))))) + (e-block + (statements + (e-record))))))) + (e-binop (op "*") + (e-ident (raw "count_")) + (e-ident (raw "sum_")))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "countEvens")) + (e-int (raw "150")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "countEvens")) + (e-block + (s-var + (p-assign (ident "count_")) + (e-num (value "0"))) + (s-var + (p-assign (ident "sum_")) + (e-num (value "0"))) + (s-for + (p-assign (ident "n")) + (e-list + (elems + (e-num (value "1")) + (e-num (value "2")) + (e-num (value "3")) + (e-num (value "4")) + (e-num (value "5")) + (e-num (value "6")) + (e-num (value "7")) + (e-num (value "8")) + (e-num (value "9")) + (e-num (value "10")))) + (e-block + (e-if + (if-branches + (if-branch + (e-binop (op "eq") + (e-binop (op "rem") + (e-lookup-local + (p-assign (ident "n"))) + (e-num (value "2"))) + (e-num (value "0"))) + (e-block + (s-reassign + (p-assign (ident "count_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "count_"))) + (e-num (value "1")))) + (s-reassign + (p-assign (ident "sum_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "sum_"))) + (e-lookup-local + (p-assign (ident "n"))))) + (e-empty_record)))) + (if-else + (e-block + (e-empty_record)))))) + (e-binop (op "mul") + (e-lookup-local + (p-assign (ident "count_"))) + (e-lookup-local + (p-assign (ident "sum_"))))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "countEvens"))) + (e-num (value "150"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_empty_list.md b/test/snapshots/statement/for_loop_empty_list.md new file mode 100644 index 0000000000..6fcdc9a44e --- /dev/null +++ b/test/snapshots/statement/for_loop_empty_list.md @@ -0,0 +1,102 @@ +# META +~~~ini +description=For loop with empty list +type=snippet +~~~ +# SOURCE +~~~roc +unchanged : U64 +unchanged = { + var value_ = 42 + for n in [] { + value_ = n + } + value_ +} + +expect unchanged == 42 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,CloseSquare,OpenCurly, +LowerIdent,OpAssign,LowerIdent, +CloseCurly, +LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "unchanged") + (ty (name "U64"))) + (s-decl + (p-ident (raw "unchanged")) + (e-block + (statements + (s-var (name "value_") + (e-int (raw "42"))) + (s-for + (p-ident (raw "n")) + (e-list) + (e-block + (statements + (s-decl + (p-ident (raw "value_")) + (e-ident (raw "n")))))) + (e-ident (raw "value_"))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "unchanged")) + (e-int (raw "42")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "unchanged")) + (e-block + (s-var + (p-assign (ident "value_")) + (e-num (value "42"))) + (s-for + (p-assign (ident "n")) + (e-empty_list) + (e-block + (s-reassign + (p-assign (ident "value_")) + (e-lookup-local + (p-assign (ident "n")))) + (e-empty_record))) + (e-lookup-local + (p-assign (ident "value_")))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "unchanged"))) + (e-num (value "42"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_list_bool.md b/test/snapshots/statement/for_loop_list_bool.md new file mode 100644 index 0000000000..c6c1cb1e05 --- /dev/null +++ b/test/snapshots/statement/for_loop_list_bool.md @@ -0,0 +1,150 @@ +# META +~~~ini +description=For loop iterating over List Bool +type=snippet +~~~ +# SOURCE +~~~roc +result : Bool +result = { + var allTrue_ = Bool.True + for b in [Bool.True, Bool.True, Bool.False] { + if b == Bool.False { + allTrue_ = Bool.False + } else { + {} + } + } + allTrue_ +} + +expect result == Bool.False +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent, +KwFor,LowerIdent,KwIn,OpenSquare,UpperIdent,NoSpaceDotUpperIdent,Comma,UpperIdent,NoSpaceDotUpperIdent,Comma,UpperIdent,NoSpaceDotUpperIdent,CloseSquare,OpenCurly, +KwIf,LowerIdent,OpEquals,UpperIdent,NoSpaceDotUpperIdent,OpenCurly, +LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent, +CloseCurly,KwElse,OpenCurly, +OpenCurly,CloseCurly, +CloseCurly, +CloseCurly, +LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,UpperIdent,NoSpaceDotUpperIdent, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "result") + (ty (name "Bool"))) + (s-decl + (p-ident (raw "result")) + (e-block + (statements + (s-var (name "allTrue_") + (e-tag (raw "Bool.True"))) + (s-for + (p-ident (raw "b")) + (e-list + (e-tag (raw "Bool.True")) + (e-tag (raw "Bool.True")) + (e-tag (raw "Bool.False"))) + (e-block + (statements + (e-if-then-else + (e-binop (op "==") + (e-ident (raw "b")) + (e-tag (raw "Bool.False"))) + (e-block + (statements + (s-decl + (p-ident (raw "allTrue_")) + (e-tag (raw "Bool.False"))))) + (e-block + (statements + (e-record))))))) + (e-ident (raw "allTrue_"))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "result")) + (e-tag (raw "Bool.False")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "result")) + (e-block + (s-var + (p-assign (ident "allTrue_")) + (e-nominal-external + (external-module "Bool") + (e-tag (name "True")))) + (s-for + (p-assign (ident "b")) + (e-list + (elems + (e-nominal-external + (external-module "Bool") + (e-tag (name "True"))) + (e-nominal-external + (external-module "Bool") + (e-tag (name "True"))) + (e-nominal-external + (external-module "Bool") + (e-tag (name "False"))))) + (e-block + (e-if + (if-branches + (if-branch + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "b"))) + (e-nominal-external + (external-module "Bool") + (e-tag (name "False")))) + (e-block + (s-reassign + (p-assign (ident "allTrue_")) + (e-nominal-external + (external-module "Bool") + (e-tag (name "False")))) + (e-empty_record)))) + (if-else + (e-block + (e-empty_record)))))) + (e-lookup-local + (p-assign (ident "allTrue_")))) + (annotation + (ty-lookup (name "Bool") (external-module "Bool")))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "result"))) + (e-nominal-external + (external-module "Bool") + (e-tag (name "False")))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Bool"))) + (expressions + (expr (type "Bool")))) +~~~ diff --git a/test/snapshots/statement/for_loop_list_str.md b/test/snapshots/statement/for_loop_list_str.md new file mode 100644 index 0000000000..9f05f69d98 --- /dev/null +++ b/test/snapshots/statement/for_loop_list_str.md @@ -0,0 +1,119 @@ +# META +~~~ini +description=For loop iterating over List Str +type=snippet +~~~ +# SOURCE +~~~roc +count : U64 +count = { + var counter_ = 0 + for _ in ["hello", "world", "test"] { + counter_ = counter_ + 1 + } + counter_ +} + +expect count == 3 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwFor,Underscore,KwIn,OpenSquare,StringStart,StringPart,StringEnd,Comma,StringStart,StringPart,StringEnd,Comma,StringStart,StringPart,StringEnd,CloseSquare,OpenCurly, +LowerIdent,OpAssign,LowerIdent,OpPlus,Int, +CloseCurly, +LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "count") + (ty (name "U64"))) + (s-decl + (p-ident (raw "count")) + (e-block + (statements + (s-var (name "counter_") + (e-int (raw "0"))) + (s-for + (p-underscore) + (e-list + (e-string + (e-string-part (raw "hello"))) + (e-string + (e-string-part (raw "world"))) + (e-string + (e-string-part (raw "test")))) + (e-block + (statements + (s-decl + (p-ident (raw "counter_")) + (e-binop (op "+") + (e-ident (raw "counter_")) + (e-int (raw "1"))))))) + (e-ident (raw "counter_"))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "count")) + (e-int (raw "3")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "count")) + (e-block + (s-var + (p-assign (ident "counter_")) + (e-num (value "0"))) + (s-for + (p-underscore) + (e-list + (elems + (e-string + (e-literal (string "hello"))) + (e-string + (e-literal (string "world"))) + (e-string + (e-literal (string "test"))))) + (e-block + (s-reassign + (p-assign (ident "counter_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "counter_"))) + (e-num (value "1")))) + (e-empty_record))) + (e-lookup-local + (p-assign (ident "counter_")))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "count"))) + (e-num (value "3"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_list_u64.md b/test/snapshots/statement/for_loop_list_u64.md new file mode 100644 index 0000000000..80e16a4d98 --- /dev/null +++ b/test/snapshots/statement/for_loop_list_u64.md @@ -0,0 +1,118 @@ +# META +~~~ini +description=For loop iterating over List U64 +type=snippet +~~~ +# SOURCE +~~~roc +sum : U64 +sum = { + var total_ = 0 + for n in [1, 2, 3, 4, 5] { + total_ = total_ + n + } + total_ +} + +expect sum == 15 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,CloseSquare,OpenCurly, +LowerIdent,OpAssign,LowerIdent,OpPlus,LowerIdent, +CloseCurly, +LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "sum") + (ty (name "U64"))) + (s-decl + (p-ident (raw "sum")) + (e-block + (statements + (s-var (name "total_") + (e-int (raw "0"))) + (s-for + (p-ident (raw "n")) + (e-list + (e-int (raw "1")) + (e-int (raw "2")) + (e-int (raw "3")) + (e-int (raw "4")) + (e-int (raw "5"))) + (e-block + (statements + (s-decl + (p-ident (raw "total_")) + (e-binop (op "+") + (e-ident (raw "total_")) + (e-ident (raw "n"))))))) + (e-ident (raw "total_"))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "sum")) + (e-int (raw "15")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "sum")) + (e-block + (s-var + (p-assign (ident "total_")) + (e-num (value "0"))) + (s-for + (p-assign (ident "n")) + (e-list + (elems + (e-num (value "1")) + (e-num (value "2")) + (e-num (value "3")) + (e-num (value "4")) + (e-num (value "5")))) + (e-block + (s-reassign + (p-assign (ident "total_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "total_"))) + (e-lookup-local + (p-assign (ident "n"))))) + (e-empty_record))) + (e-lookup-local + (p-assign (ident "total_")))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "sum"))) + (e-num (value "15"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_nested.md b/test/snapshots/statement/for_loop_nested.md new file mode 100644 index 0000000000..dbb0d8d4af --- /dev/null +++ b/test/snapshots/statement/for_loop_nested.md @@ -0,0 +1,139 @@ +# META +~~~ini +description=Nested for loops +type=snippet +~~~ +# SOURCE +~~~roc +product : U64 +product = { + var result_ = 0 + for i in [1, 2, 3] { + for j in [10, 20] { + result_ = result_ + (i * j) + } + } + result_ +} + +expect product == 180 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,Comma,Int,CloseSquare,OpenCurly, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,CloseSquare,OpenCurly, +LowerIdent,OpAssign,LowerIdent,OpPlus,OpenRound,LowerIdent,OpStar,LowerIdent,CloseRound, +CloseCurly, +CloseCurly, +LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "product") + (ty (name "U64"))) + (s-decl + (p-ident (raw "product")) + (e-block + (statements + (s-var (name "result_") + (e-int (raw "0"))) + (s-for + (p-ident (raw "i")) + (e-list + (e-int (raw "1")) + (e-int (raw "2")) + (e-int (raw "3"))) + (e-block + (statements + (s-for + (p-ident (raw "j")) + (e-list + (e-int (raw "10")) + (e-int (raw "20"))) + (e-block + (statements + (s-decl + (p-ident (raw "result_")) + (e-binop (op "+") + (e-ident (raw "result_")) + (e-tuple + (e-binop (op "*") + (e-ident (raw "i")) + (e-ident (raw "j")))))))))))) + (e-ident (raw "result_"))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "product")) + (e-int (raw "180")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "product")) + (e-block + (s-var + (p-assign (ident "result_")) + (e-num (value "0"))) + (s-for + (p-assign (ident "i")) + (e-list + (elems + (e-num (value "1")) + (e-num (value "2")) + (e-num (value "3")))) + (e-block + (s-for + (p-assign (ident "j")) + (e-list + (elems + (e-num (value "10")) + (e-num (value "20")))) + (e-block + (s-reassign + (p-assign (ident "result_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "result_"))) + (e-binop (op "mul") + (e-lookup-local + (p-assign (ident "i"))) + (e-lookup-local + (p-assign (ident "j")))))) + (e-empty_record))) + (e-empty_record))) + (e-lookup-local + (p-assign (ident "result_")))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "product"))) + (e-num (value "180"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_var_conditional_persist.md b/test/snapshots/statement/for_loop_var_conditional_persist.md new file mode 100644 index 0000000000..13b89f8dd1 --- /dev/null +++ b/test/snapshots/statement/for_loop_var_conditional_persist.md @@ -0,0 +1,176 @@ +# META +~~~ini +description=For loop with var that persists across iterations with conditional updates +type=snippet +~~~ +# SOURCE +~~~roc +result : U64 +result = { + var lastEven_ = 0 + var evenCount_ = 0 + for n in [1, 2, 3, 4, 5, 6, 7, 8] { + if n % 2 == 0 { + lastEven_ = n + evenCount_ = evenCount_ + 1 + } else { + {} + } + } + lastEven_ * evenCount_ +} + +expect result == 32 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,CloseSquare,OpenCurly, +KwIf,LowerIdent,OpPercent,Int,OpEquals,Int,OpenCurly, +LowerIdent,OpAssign,LowerIdent, +LowerIdent,OpAssign,LowerIdent,OpPlus,Int, +CloseCurly,KwElse,OpenCurly, +OpenCurly,CloseCurly, +CloseCurly, +CloseCurly, +LowerIdent,OpStar,LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "result") + (ty (name "U64"))) + (s-decl + (p-ident (raw "result")) + (e-block + (statements + (s-var (name "lastEven_") + (e-int (raw "0"))) + (s-var (name "evenCount_") + (e-int (raw "0"))) + (s-for + (p-ident (raw "n")) + (e-list + (e-int (raw "1")) + (e-int (raw "2")) + (e-int (raw "3")) + (e-int (raw "4")) + (e-int (raw "5")) + (e-int (raw "6")) + (e-int (raw "7")) + (e-int (raw "8"))) + (e-block + (statements + (e-if-then-else + (e-binop (op "==") + (e-binop (op "%") + (e-ident (raw "n")) + (e-int (raw "2"))) + (e-int (raw "0"))) + (e-block + (statements + (s-decl + (p-ident (raw "lastEven_")) + (e-ident (raw "n"))) + (s-decl + (p-ident (raw "evenCount_")) + (e-binop (op "+") + (e-ident (raw "evenCount_")) + (e-int (raw "1")))))) + (e-block + (statements + (e-record))))))) + (e-binop (op "*") + (e-ident (raw "lastEven_")) + (e-ident (raw "evenCount_")))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "result")) + (e-int (raw "32")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "result")) + (e-block + (s-var + (p-assign (ident "lastEven_")) + (e-num (value "0"))) + (s-var + (p-assign (ident "evenCount_")) + (e-num (value "0"))) + (s-for + (p-assign (ident "n")) + (e-list + (elems + (e-num (value "1")) + (e-num (value "2")) + (e-num (value "3")) + (e-num (value "4")) + (e-num (value "5")) + (e-num (value "6")) + (e-num (value "7")) + (e-num (value "8")))) + (e-block + (e-if + (if-branches + (if-branch + (e-binop (op "eq") + (e-binop (op "rem") + (e-lookup-local + (p-assign (ident "n"))) + (e-num (value "2"))) + (e-num (value "0"))) + (e-block + (s-reassign + (p-assign (ident "lastEven_")) + (e-lookup-local + (p-assign (ident "n")))) + (s-reassign + (p-assign (ident "evenCount_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "evenCount_"))) + (e-num (value "1")))) + (e-empty_record)))) + (if-else + (e-block + (e-empty_record)))))) + (e-binop (op "mul") + (e-lookup-local + (p-assign (ident "lastEven_"))) + (e-lookup-local + (p-assign (ident "evenCount_"))))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "result"))) + (e-num (value "32"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_var_every_iteration.md b/test/snapshots/statement/for_loop_var_every_iteration.md new file mode 100644 index 0000000000..e796adb3ae --- /dev/null +++ b/test/snapshots/statement/for_loop_var_every_iteration.md @@ -0,0 +1,138 @@ +# META +~~~ini +description=For loop with var reassignment on every iteration +type=snippet +~~~ +# SOURCE +~~~roc +result : U64 +result = { + var prev_ = 0 + var count_ = 0 + for n in [10, 20, 30, 40, 50] { + count_ = count_ + 1 + prev_ = n + } + prev_ + count_ +} + +expect result == 55 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,CloseSquare,OpenCurly, +LowerIdent,OpAssign,LowerIdent,OpPlus,Int, +LowerIdent,OpAssign,LowerIdent, +CloseCurly, +LowerIdent,OpPlus,LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "result") + (ty (name "U64"))) + (s-decl + (p-ident (raw "result")) + (e-block + (statements + (s-var (name "prev_") + (e-int (raw "0"))) + (s-var (name "count_") + (e-int (raw "0"))) + (s-for + (p-ident (raw "n")) + (e-list + (e-int (raw "10")) + (e-int (raw "20")) + (e-int (raw "30")) + (e-int (raw "40")) + (e-int (raw "50"))) + (e-block + (statements + (s-decl + (p-ident (raw "count_")) + (e-binop (op "+") + (e-ident (raw "count_")) + (e-int (raw "1")))) + (s-decl + (p-ident (raw "prev_")) + (e-ident (raw "n")))))) + (e-binop (op "+") + (e-ident (raw "prev_")) + (e-ident (raw "count_")))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "result")) + (e-int (raw "55")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "result")) + (e-block + (s-var + (p-assign (ident "prev_")) + (e-num (value "0"))) + (s-var + (p-assign (ident "count_")) + (e-num (value "0"))) + (s-for + (p-assign (ident "n")) + (e-list + (elems + (e-num (value "10")) + (e-num (value "20")) + (e-num (value "30")) + (e-num (value "40")) + (e-num (value "50")))) + (e-block + (s-reassign + (p-assign (ident "count_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "count_"))) + (e-num (value "1")))) + (s-reassign + (p-assign (ident "prev_")) + (e-lookup-local + (p-assign (ident "n")))) + (e-empty_record))) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "prev_"))) + (e-lookup-local + (p-assign (ident "count_"))))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "result"))) + (e-num (value "55"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~ diff --git a/test/snapshots/statement/for_loop_var_reassign_tracking.md b/test/snapshots/statement/for_loop_var_reassign_tracking.md new file mode 100644 index 0000000000..9b3209a49d --- /dev/null +++ b/test/snapshots/statement/for_loop_var_reassign_tracking.md @@ -0,0 +1,168 @@ +# META +~~~ini +description=For loop with var reassignment tracking across iterations +type=snippet +~~~ +# SOURCE +~~~roc +result : U64 +result = { + var sum_ = 0 + var max_ = 0 + for n in [3, 7, 2, 9, 1] { + sum_ = sum_ + n + if n > max_ { + max_ = n + } else { + {} + } + } + sum_ + max_ +} + +expect result == 31 +~~~ +# EXPECTED +NIL +# PROBLEMS +NIL +# TOKENS +~~~zig +LowerIdent,OpColon,UpperIdent, +LowerIdent,OpAssign,OpenCurly, +KwVar,LowerIdent,OpAssign,Int, +KwVar,LowerIdent,OpAssign,Int, +KwFor,LowerIdent,KwIn,OpenSquare,Int,Comma,Int,Comma,Int,Comma,Int,Comma,Int,CloseSquare,OpenCurly, +LowerIdent,OpAssign,LowerIdent,OpPlus,LowerIdent, +KwIf,LowerIdent,OpGreaterThan,LowerIdent,OpenCurly, +LowerIdent,OpAssign,LowerIdent, +CloseCurly,KwElse,OpenCurly, +OpenCurly,CloseCurly, +CloseCurly, +CloseCurly, +LowerIdent,OpPlus,LowerIdent, +CloseCurly, +KwExpect,LowerIdent,OpEquals,Int, +EndOfFile, +~~~ +# PARSE +~~~clojure +(file + (type-module) + (statements + (s-type-anno (name "result") + (ty (name "U64"))) + (s-decl + (p-ident (raw "result")) + (e-block + (statements + (s-var (name "sum_") + (e-int (raw "0"))) + (s-var (name "max_") + (e-int (raw "0"))) + (s-for + (p-ident (raw "n")) + (e-list + (e-int (raw "3")) + (e-int (raw "7")) + (e-int (raw "2")) + (e-int (raw "9")) + (e-int (raw "1"))) + (e-block + (statements + (s-decl + (p-ident (raw "sum_")) + (e-binop (op "+") + (e-ident (raw "sum_")) + (e-ident (raw "n")))) + (e-if-then-else + (e-binop (op ">") + (e-ident (raw "n")) + (e-ident (raw "max_"))) + (e-block + (statements + (s-decl + (p-ident (raw "max_")) + (e-ident (raw "n"))))) + (e-block + (statements + (e-record))))))) + (e-binop (op "+") + (e-ident (raw "sum_")) + (e-ident (raw "max_")))))) + (s-expect + (e-binop (op "==") + (e-ident (raw "result")) + (e-int (raw "31")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign (ident "result")) + (e-block + (s-var + (p-assign (ident "sum_")) + (e-num (value "0"))) + (s-var + (p-assign (ident "max_")) + (e-num (value "0"))) + (s-for + (p-assign (ident "n")) + (e-list + (elems + (e-num (value "3")) + (e-num (value "7")) + (e-num (value "2")) + (e-num (value "9")) + (e-num (value "1")))) + (e-block + (s-reassign + (p-assign (ident "sum_")) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "sum_"))) + (e-lookup-local + (p-assign (ident "n"))))) + (e-if + (if-branches + (if-branch + (e-binop (op "gt") + (e-lookup-local + (p-assign (ident "n"))) + (e-lookup-local + (p-assign (ident "max_")))) + (e-block + (s-reassign + (p-assign (ident "max_")) + (e-lookup-local + (p-assign (ident "n")))) + (e-empty_record)))) + (if-else + (e-block + (e-empty_record)))))) + (e-binop (op "add") + (e-lookup-local + (p-assign (ident "sum_"))) + (e-lookup-local + (p-assign (ident "max_"))))) + (annotation + (ty-lookup (name "U64") (builtin)))) + (s-expect + (e-binop (op "eq") + (e-lookup-local + (p-assign (ident "result"))) + (e-num (value "31"))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt (type "Num(Int(Unsigned64))"))) + (expressions + (expr (type "Num(Int(Unsigned64))")))) +~~~