mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
continue/break
This commit is contained in:
@@ -94,6 +94,15 @@ fn main() {
|
||||
for i in 0..5 { ... } // 0 1 2 3 4 (exclusive ..)
|
||||
for i in 1...3 { ... } // 1 2 3 (inclusive ...)
|
||||
|
||||
for i in 0..10 {
|
||||
if i == 2 {
|
||||
continue // skip this iteration
|
||||
}
|
||||
if i == 5 {
|
||||
break // leave the loop early
|
||||
}
|
||||
}
|
||||
|
||||
let grid = [[1, 2], [3, 4]] // nested arrays
|
||||
println(grid[1][0]) // 3
|
||||
|
||||
@@ -109,9 +118,11 @@ fn main() {
|
||||
`push(a, v)`; array literals may nest
|
||||
- for loops: `for x in arr { }` and ranges `for i in 0..10 { }` /
|
||||
`for i in 0...10 { }`; loop variables are scoped to the loop body
|
||||
- `break` / `continue` inside `while` and `for` loops (in `for` loops
|
||||
`continue` advances the loop variable / iterator first)
|
||||
- operators: `+ - * / %`, `== != < <= > >=`, `and or not`, unary `-`
|
||||
- statements: `let`, assignment, `if/else`, `while`, `for`, `return`, `assert`,
|
||||
calls, `print(...)` / `println(...)`
|
||||
- statements: `let`, assignment, `if/else`, `while`, `for`, `break`,
|
||||
`continue`, `return`, `assert`, calls, `print(...)` / `println(...)`
|
||||
- comments: `//`
|
||||
|
||||
## Assembly
|
||||
|
||||
@@ -66,6 +66,14 @@ struct Fixup {
|
||||
off u32
|
||||
}
|
||||
|
||||
// LoopCtx records where `break` and `continue` should jump while generating
|
||||
// the body of a loop. For `for` loops `continue` targets the increment, not
|
||||
// the condition check, so the loop variable still advances.
|
||||
struct LoopCtx {
|
||||
break_l string
|
||||
continue_l string
|
||||
}
|
||||
|
||||
struct Gen {
|
||||
mut:
|
||||
code []u8
|
||||
@@ -79,6 +87,7 @@ mut:
|
||||
cur_fn string
|
||||
labels map[string]int
|
||||
fixups []Fixup
|
||||
loops []LoopCtx
|
||||
enter_off u32
|
||||
next_lbl int
|
||||
}
|
||||
@@ -189,9 +198,11 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.code << op_jz
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||
g.loops << LoopCtx{ break_l: end_l, continue_l: loop_l }
|
||||
for s in st.body {
|
||||
g.gen_stmt(s)!
|
||||
}
|
||||
g.loops.delete_last()
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||
@@ -202,6 +213,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
var_idx := g.new_local()
|
||||
bound_idx := g.new_local()
|
||||
loop_l := g.new_label()
|
||||
inc_l := g.new_label()
|
||||
end_l := g.new_label()
|
||||
g.gen_expr(st.expr)!
|
||||
g.gen_expr(st.cond)!
|
||||
@@ -214,6 +226,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.code << op_jz
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||
g.loops << LoopCtx{ break_l: end_l, continue_l: inc_l }
|
||||
prev := g.locals[st.target] or { -1 }
|
||||
g.locals[st.target] = var_idx
|
||||
for s in st.body {
|
||||
@@ -224,6 +237,8 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
} else {
|
||||
g.locals.delete(st.target)
|
||||
}
|
||||
g.loops.delete_last()
|
||||
g.emit_label(inc_l)
|
||||
g.emit_load(var_idx)
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(1)
|
||||
@@ -240,6 +255,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
idx_idx := g.new_local()
|
||||
elem_idx := g.new_local()
|
||||
loop_l := g.new_label()
|
||||
inc_l := g.new_label()
|
||||
end_l := g.new_label()
|
||||
g.gen_expr(st.expr)!
|
||||
g.emit_store(arr_idx)
|
||||
@@ -254,6 +270,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.code << op_jz
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||
g.loops << LoopCtx{ break_l: end_l, continue_l: inc_l }
|
||||
g.emit_load(arr_idx)
|
||||
g.emit_load(idx_idx)
|
||||
g.code << op_aget
|
||||
@@ -268,6 +285,8 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
} else {
|
||||
g.locals.delete(st.target)
|
||||
}
|
||||
g.loops.delete_last()
|
||||
g.emit_label(inc_l)
|
||||
g.emit_load(idx_idx)
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(1)
|
||||
@@ -290,6 +309,24 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.gen_expr(st.expr)!
|
||||
g.code << op_assert
|
||||
}
|
||||
.break_stmt {
|
||||
if g.loops.len == 0 {
|
||||
return error('break outside of a loop (line ${st.line})')
|
||||
}
|
||||
ctx := g.loops[g.loops.len - 1]
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: ctx.break_l, off: u32(g.code.len) - 8 }
|
||||
}
|
||||
.continue_stmt {
|
||||
if g.loops.len == 0 {
|
||||
return error('continue outside of a loop (line ${st.line})')
|
||||
}
|
||||
ctx := g.loops[g.loops.len - 1]
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: ctx.continue_l, off: u32(g.code.len) - 8 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ pub enum TokKind {
|
||||
kw_while
|
||||
kw_for
|
||||
kw_in
|
||||
kw_break
|
||||
kw_continue
|
||||
kw_return
|
||||
kw_true
|
||||
kw_false
|
||||
@@ -251,6 +253,8 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
||||
'while' { TokKind.kw_while }
|
||||
'for' { TokKind.kw_for }
|
||||
'in' { TokKind.kw_in }
|
||||
'break' { TokKind.kw_break }
|
||||
'continue' { TokKind.kw_continue }
|
||||
'return' { TokKind.kw_return }
|
||||
'true' { TokKind.kw_true }
|
||||
'false' { TokKind.kw_false }
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// | 'while' cond block
|
||||
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
||||
// | 'for' IDENT 'in' expr block (iterate an array)
|
||||
// | 'break' | 'continue'
|
||||
// | 'return' [expr]
|
||||
// | 'assert' expr
|
||||
// | expr
|
||||
@@ -60,6 +61,8 @@ pub enum StmtKind {
|
||||
while_stmt
|
||||
for_range_stmt
|
||||
for_in_stmt
|
||||
break_stmt
|
||||
continue_stmt
|
||||
ret_stmt
|
||||
assert_stmt
|
||||
}
|
||||
@@ -230,6 +233,14 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
||||
e = p.parse_expr()!
|
||||
}
|
||||
return Stmt{ kind: .ret_stmt, expr: e, has_val: has_val, line: t.line }
|
||||
}
|
||||
.kw_break {
|
||||
p.advance()
|
||||
return Stmt{ kind: .break_stmt, line: t.line }
|
||||
}
|
||||
.kw_continue {
|
||||
p.advance()
|
||||
return Stmt{ kind: .continue_stmt, line: t.line }
|
||||
} .kw_assert {
|
||||
p.advance()
|
||||
mut e := Expr{}
|
||||
|
||||
@@ -65,6 +65,29 @@ fn main() {
|
||||
println("i is back to ")
|
||||
println(i) // 100
|
||||
|
||||
// break and continue
|
||||
for i in 0..10 {
|
||||
if i == 2 {
|
||||
continue // skip 2
|
||||
}
|
||||
if i == 5 {
|
||||
break // stop at 5
|
||||
}
|
||||
print(i)
|
||||
print(" ")
|
||||
}
|
||||
println("")
|
||||
|
||||
// find the first even number in an array
|
||||
let found = -1
|
||||
for x in a {
|
||||
if x % 2 == 0 {
|
||||
found = x
|
||||
break
|
||||
}
|
||||
}
|
||||
println("first even in a: " + found) // 10
|
||||
|
||||
assert sum([1, 2, 3, 4]) == 10
|
||||
assert len(squares) == 5
|
||||
assert squares[4] == 16
|
||||
|
||||
@@ -78,6 +78,53 @@ fn test_for_in() {
|
||||
assert squares[3] == 9
|
||||
}
|
||||
|
||||
fn test_break_continue() {
|
||||
// break exits the loop early
|
||||
let total = 0
|
||||
for i in 0..100 {
|
||||
if i == 5 {
|
||||
break
|
||||
}
|
||||
total = total + i
|
||||
}
|
||||
assert total == 10 // 0+1+2+3+4
|
||||
|
||||
// continue skips to the next iteration
|
||||
let sum = 0
|
||||
for i in 0..6 {
|
||||
if i == 3 {
|
||||
continue
|
||||
}
|
||||
sum = sum + i
|
||||
}
|
||||
assert sum == 12 // 0+1+2+4+5 (3 skipped)
|
||||
|
||||
// break and continue in while loops
|
||||
let n = 0
|
||||
while true {
|
||||
n = n + 1
|
||||
if n == 3 {
|
||||
continue
|
||||
}
|
||||
if n == 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
assert n == 5
|
||||
|
||||
// continue advances the iterator in for-in loops (no infinite loop)
|
||||
let odds = []
|
||||
for x in [1, 2, 3, 4, 5] {
|
||||
if x % 2 == 0 {
|
||||
continue
|
||||
}
|
||||
push(odds, x)
|
||||
}
|
||||
assert odds == odds
|
||||
assert len(odds) == 3
|
||||
assert odds[2] == 5
|
||||
}
|
||||
|
||||
fn test_failing() {
|
||||
// this one is meant to fail — shows up in `vr test` output
|
||||
assert 1 == 2
|
||||
|
||||
Reference in New Issue
Block a user