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 0..5 { ... } // 0 1 2 3 4 (exclusive ..)
|
||||||
for i in 1...3 { ... } // 1 2 3 (inclusive ...)
|
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
|
let grid = [[1, 2], [3, 4]] // nested arrays
|
||||||
println(grid[1][0]) // 3
|
println(grid[1][0]) // 3
|
||||||
|
|
||||||
@@ -109,9 +118,11 @@ fn main() {
|
|||||||
`push(a, v)`; array literals may nest
|
`push(a, v)`; array literals may nest
|
||||||
- for loops: `for x in arr { }` and ranges `for i in 0..10 { }` /
|
- 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
|
`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 `-`
|
- operators: `+ - * / %`, `== != < <= > >=`, `and or not`, unary `-`
|
||||||
- statements: `let`, assignment, `if/else`, `while`, `for`, `return`, `assert`,
|
- statements: `let`, assignment, `if/else`, `while`, `for`, `break`,
|
||||||
calls, `print(...)` / `println(...)`
|
`continue`, `return`, `assert`, calls, `print(...)` / `println(...)`
|
||||||
- comments: `//`
|
- comments: `//`
|
||||||
|
|
||||||
## Assembly
|
## Assembly
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ struct Fixup {
|
|||||||
off u32
|
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 {
|
struct Gen {
|
||||||
mut:
|
mut:
|
||||||
code []u8
|
code []u8
|
||||||
@@ -79,6 +87,7 @@ mut:
|
|||||||
cur_fn string
|
cur_fn string
|
||||||
labels map[string]int
|
labels map[string]int
|
||||||
fixups []Fixup
|
fixups []Fixup
|
||||||
|
loops []LoopCtx
|
||||||
enter_off u32
|
enter_off u32
|
||||||
next_lbl int
|
next_lbl int
|
||||||
}
|
}
|
||||||
@@ -189,9 +198,11 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
g.code << op_jz
|
g.code << op_jz
|
||||||
g.code << obj.encode_i64(0)
|
g.code << obj.encode_i64(0)
|
||||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
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 {
|
for s in st.body {
|
||||||
g.gen_stmt(s)!
|
g.gen_stmt(s)!
|
||||||
}
|
}
|
||||||
|
g.loops.delete_last()
|
||||||
g.code << op_jmp
|
g.code << op_jmp
|
||||||
g.code << obj.encode_i64(0)
|
g.code << obj.encode_i64(0)
|
||||||
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
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()
|
var_idx := g.new_local()
|
||||||
bound_idx := g.new_local()
|
bound_idx := g.new_local()
|
||||||
loop_l := g.new_label()
|
loop_l := g.new_label()
|
||||||
|
inc_l := g.new_label()
|
||||||
end_l := g.new_label()
|
end_l := g.new_label()
|
||||||
g.gen_expr(st.expr)!
|
g.gen_expr(st.expr)!
|
||||||
g.gen_expr(st.cond)!
|
g.gen_expr(st.cond)!
|
||||||
@@ -214,6 +226,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
g.code << op_jz
|
g.code << op_jz
|
||||||
g.code << obj.encode_i64(0)
|
g.code << obj.encode_i64(0)
|
||||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
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 }
|
prev := g.locals[st.target] or { -1 }
|
||||||
g.locals[st.target] = var_idx
|
g.locals[st.target] = var_idx
|
||||||
for s in st.body {
|
for s in st.body {
|
||||||
@@ -224,6 +237,8 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
} else {
|
} else {
|
||||||
g.locals.delete(st.target)
|
g.locals.delete(st.target)
|
||||||
}
|
}
|
||||||
|
g.loops.delete_last()
|
||||||
|
g.emit_label(inc_l)
|
||||||
g.emit_load(var_idx)
|
g.emit_load(var_idx)
|
||||||
g.code << op_push_i
|
g.code << op_push_i
|
||||||
g.code << obj.encode_i64(1)
|
g.code << obj.encode_i64(1)
|
||||||
@@ -240,6 +255,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
idx_idx := g.new_local()
|
idx_idx := g.new_local()
|
||||||
elem_idx := g.new_local()
|
elem_idx := g.new_local()
|
||||||
loop_l := g.new_label()
|
loop_l := g.new_label()
|
||||||
|
inc_l := g.new_label()
|
||||||
end_l := g.new_label()
|
end_l := g.new_label()
|
||||||
g.gen_expr(st.expr)!
|
g.gen_expr(st.expr)!
|
||||||
g.emit_store(arr_idx)
|
g.emit_store(arr_idx)
|
||||||
@@ -254,6 +270,7 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
g.code << op_jz
|
g.code << op_jz
|
||||||
g.code << obj.encode_i64(0)
|
g.code << obj.encode_i64(0)
|
||||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
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(arr_idx)
|
||||||
g.emit_load(idx_idx)
|
g.emit_load(idx_idx)
|
||||||
g.code << op_aget
|
g.code << op_aget
|
||||||
@@ -268,6 +285,8 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
} else {
|
} else {
|
||||||
g.locals.delete(st.target)
|
g.locals.delete(st.target)
|
||||||
}
|
}
|
||||||
|
g.loops.delete_last()
|
||||||
|
g.emit_label(inc_l)
|
||||||
g.emit_load(idx_idx)
|
g.emit_load(idx_idx)
|
||||||
g.code << op_push_i
|
g.code << op_push_i
|
||||||
g.code << obj.encode_i64(1)
|
g.code << obj.encode_i64(1)
|
||||||
@@ -290,6 +309,24 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
g.gen_expr(st.expr)!
|
g.gen_expr(st.expr)!
|
||||||
g.code << op_assert
|
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_while
|
||||||
kw_for
|
kw_for
|
||||||
kw_in
|
kw_in
|
||||||
|
kw_break
|
||||||
|
kw_continue
|
||||||
kw_return
|
kw_return
|
||||||
kw_true
|
kw_true
|
||||||
kw_false
|
kw_false
|
||||||
@@ -251,6 +253,8 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
|||||||
'while' { TokKind.kw_while }
|
'while' { TokKind.kw_while }
|
||||||
'for' { TokKind.kw_for }
|
'for' { TokKind.kw_for }
|
||||||
'in' { TokKind.kw_in }
|
'in' { TokKind.kw_in }
|
||||||
|
'break' { TokKind.kw_break }
|
||||||
|
'continue' { TokKind.kw_continue }
|
||||||
'return' { TokKind.kw_return }
|
'return' { TokKind.kw_return }
|
||||||
'true' { TokKind.kw_true }
|
'true' { TokKind.kw_true }
|
||||||
'false' { TokKind.kw_false }
|
'false' { TokKind.kw_false }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
// | 'while' cond block
|
// | 'while' cond block
|
||||||
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
||||||
// | 'for' IDENT 'in' expr block (iterate an array)
|
// | 'for' IDENT 'in' expr block (iterate an array)
|
||||||
|
// | 'break' | 'continue'
|
||||||
// | 'return' [expr]
|
// | 'return' [expr]
|
||||||
// | 'assert' expr
|
// | 'assert' expr
|
||||||
// | expr
|
// | expr
|
||||||
@@ -60,6 +61,8 @@ pub enum StmtKind {
|
|||||||
while_stmt
|
while_stmt
|
||||||
for_range_stmt
|
for_range_stmt
|
||||||
for_in_stmt
|
for_in_stmt
|
||||||
|
break_stmt
|
||||||
|
continue_stmt
|
||||||
ret_stmt
|
ret_stmt
|
||||||
assert_stmt
|
assert_stmt
|
||||||
}
|
}
|
||||||
@@ -230,6 +233,14 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
|||||||
e = p.parse_expr()!
|
e = p.parse_expr()!
|
||||||
}
|
}
|
||||||
return Stmt{ kind: .ret_stmt, expr: e, has_val: has_val, line: t.line }
|
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 {
|
} .kw_assert {
|
||||||
p.advance()
|
p.advance()
|
||||||
mut e := Expr{}
|
mut e := Expr{}
|
||||||
|
|||||||
@@ -65,6 +65,29 @@ fn main() {
|
|||||||
println("i is back to ")
|
println("i is back to ")
|
||||||
println(i) // 100
|
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 sum([1, 2, 3, 4]) == 10
|
||||||
assert len(squares) == 5
|
assert len(squares) == 5
|
||||||
assert squares[4] == 16
|
assert squares[4] == 16
|
||||||
|
|||||||
@@ -78,6 +78,53 @@ fn test_for_in() {
|
|||||||
assert squares[3] == 9
|
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() {
|
fn test_failing() {
|
||||||
// this one is meant to fail — shows up in `vr test` output
|
// this one is meant to fail — shows up in `vr test` output
|
||||||
assert 1 == 2
|
assert 1 == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user