Merge pull request #8331 from roc-lang/for-loops

`for` loops in the interpreter
This commit is contained in:
Richard Feldman 2025-10-24 19:39:27 -04:00 committed by GitHub
commit 2b89aee96b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1496 additions and 0 deletions

View file

@ -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,
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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))"))))
~~~

View file

@ -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))"))))
~~~

View file

@ -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"))))
~~~

View file

@ -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))"))))
~~~

View file

@ -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))"))))
~~~

View file

@ -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))"))))
~~~

View file

@ -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))"))))
~~~

View file

@ -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))"))))
~~~

View file

@ -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))"))))
~~~