gen: implement labelled break and continue (#6880)
parent
41ba942369
commit
e798326a1a
|
@ -285,6 +285,7 @@ pub mut:
|
||||||
pub struct BranchStmt {
|
pub struct BranchStmt {
|
||||||
pub:
|
pub:
|
||||||
kind token.Kind
|
kind token.Kind
|
||||||
|
label string
|
||||||
pos token.Position
|
pos token.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,6 +627,8 @@ pub:
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
is_inf bool // `for {}`
|
is_inf bool // `for {}`
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
pub mut:
|
||||||
|
label string // `label: for {`
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ForInStmt {
|
pub struct ForInStmt {
|
||||||
|
@ -644,6 +647,7 @@ pub mut:
|
||||||
val_type table.Type
|
val_type table.Type
|
||||||
cond_type table.Type
|
cond_type table.Type
|
||||||
kind table.Kind // array/map/string
|
kind table.Kind // array/map/string
|
||||||
|
label string // `label: for {`
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ForCStmt {
|
pub struct ForCStmt {
|
||||||
|
@ -656,6 +660,8 @@ pub:
|
||||||
has_inc bool
|
has_inc bool
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
pub mut:
|
||||||
|
label string // `label: for {`
|
||||||
}
|
}
|
||||||
|
|
||||||
// #include etc
|
// #include etc
|
||||||
|
|
|
@ -2410,6 +2410,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
if c.in_for_count == 0 {
|
if c.in_for_count == 0 {
|
||||||
c.error('$node.kind.str() statement not within a loop', node.pos)
|
c.error('$node.kind.str() statement not within a loop', node.pos)
|
||||||
}
|
}
|
||||||
|
// TODO: check any node.label is in scope for goto
|
||||||
}
|
}
|
||||||
ast.CompFor {
|
ast.CompFor {
|
||||||
// node.typ = c.expr(node.expr)
|
// node.typ = c.expr(node.expr)
|
||||||
|
@ -2577,7 +2578,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.GotoLabel {}
|
ast.GotoLabel {}
|
||||||
ast.GotoStmt {}
|
ast.GotoStmt {
|
||||||
|
// TODO: check label doesn't bypass variable declarations
|
||||||
|
}
|
||||||
ast.HashStmt {
|
ast.HashStmt {
|
||||||
c.hash_stmt(mut node)
|
c.hash_stmt(mut node)
|
||||||
}
|
}
|
||||||
|
|
|
@ -821,9 +821,17 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.BranchStmt {
|
ast.BranchStmt {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
|
if node.label.len > 0 {
|
||||||
|
if node.kind == .key_break {
|
||||||
|
g.writeln('goto $node.label\__break;')
|
||||||
|
} else {
|
||||||
|
assert node.kind == .key_continue
|
||||||
|
g.writeln('goto $node.label\__continue;')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// continue or break
|
// continue or break
|
||||||
g.write(node.kind.str())
|
g.writeln('$node.kind;')
|
||||||
g.writeln(';')
|
}
|
||||||
}
|
}
|
||||||
ast.ConstDecl {
|
ast.ConstDecl {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
|
@ -930,6 +938,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
ast.ForCStmt {
|
ast.ForCStmt {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
g.is_vlines_enabled = false
|
g.is_vlines_enabled = false
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('$node.label:')
|
||||||
|
}
|
||||||
g.write('for (')
|
g.write('for (')
|
||||||
if !node.has_init {
|
if !node.has_init {
|
||||||
g.write('; ')
|
g.write('; ')
|
||||||
|
@ -952,7 +963,13 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
g.writeln(') {')
|
g.writeln(') {')
|
||||||
g.is_vlines_enabled = true
|
g.is_vlines_enabled = true
|
||||||
g.stmts(node.stmts)
|
g.stmts(node.stmts)
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('$node.label\__continue: {}')
|
||||||
|
}
|
||||||
g.writeln('}')
|
g.writeln('}')
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('$node.label\__break: {}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast.ForInStmt {
|
ast.ForInStmt {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
|
@ -961,6 +978,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
ast.ForStmt {
|
ast.ForStmt {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
g.is_vlines_enabled = false
|
g.is_vlines_enabled = false
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('$node.label:')
|
||||||
|
}
|
||||||
g.writeln('for (;;) {')
|
g.writeln('for (;;) {')
|
||||||
if !node.is_inf {
|
if !node.is_inf {
|
||||||
g.indent++
|
g.indent++
|
||||||
|
@ -972,7 +992,13 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
g.is_vlines_enabled = true
|
g.is_vlines_enabled = true
|
||||||
g.stmts(node.stmts)
|
g.stmts(node.stmts)
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('\t$node.label\__continue: {}')
|
||||||
|
}
|
||||||
g.writeln('}')
|
g.writeln('}')
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('$node.label\__break: {}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast.GlobalDecl {
|
ast.GlobalDecl {
|
||||||
g.global_decl(node)
|
g.global_decl(node)
|
||||||
|
@ -1097,6 +1123,9 @@ fn (mut g Gen) write_defer_stmts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) for_in(it ast.ForInStmt) {
|
fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
|
if it.label.len > 0 {
|
||||||
|
g.writeln('\t$it.label: {}')
|
||||||
|
}
|
||||||
if it.is_range {
|
if it.is_range {
|
||||||
// `for x in 1..10 {`
|
// `for x in 1..10 {`
|
||||||
i := if it.val_var == '_' { g.new_tmp_var() } else { c_name(it.val_var) }
|
i := if it.val_var == '_' { g.new_tmp_var() } else { c_name(it.val_var) }
|
||||||
|
@ -1105,8 +1134,6 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
g.write('; $i < ')
|
g.write('; $i < ')
|
||||||
g.expr(it.high)
|
g.expr(it.high)
|
||||||
g.writeln('; ++$i) {')
|
g.writeln('; ++$i) {')
|
||||||
g.stmts(it.stmts)
|
|
||||||
g.writeln('}')
|
|
||||||
} else if it.kind == .array {
|
} else if it.kind == .array {
|
||||||
// `for num in nums {`
|
// `for num in nums {`
|
||||||
g.writeln('// FOR IN array')
|
g.writeln('// FOR IN array')
|
||||||
|
@ -1136,8 +1163,6 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
g.writeln('\t$styp ${c_name(it.val_var)} = $right;')
|
g.writeln('\t$styp ${c_name(it.val_var)} = $right;')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.stmts(it.stmts)
|
|
||||||
g.writeln('}')
|
|
||||||
} else if it.kind == .array_fixed {
|
} else if it.kind == .array_fixed {
|
||||||
atmp := g.new_tmp_var()
|
atmp := g.new_tmp_var()
|
||||||
atmp_type := g.typ(it.cond_type)
|
atmp_type := g.typ(it.cond_type)
|
||||||
|
@ -1163,8 +1188,6 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
}
|
}
|
||||||
g.writeln(' = (*$atmp)[$i];')
|
g.writeln(' = (*$atmp)[$i];')
|
||||||
}
|
}
|
||||||
g.stmts(it.stmts)
|
|
||||||
g.writeln('}')
|
|
||||||
} else if it.kind == .map {
|
} else if it.kind == .map {
|
||||||
// `for key, val in map {`
|
// `for key, val in map {`
|
||||||
g.writeln('// FOR IN map')
|
g.writeln('// FOR IN map')
|
||||||
|
@ -1201,10 +1224,17 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
if it.key_type == table.string_type && !g.is_builtin_mod {
|
if it.key_type == table.string_type && !g.is_builtin_mod {
|
||||||
// g.writeln('string_free(&$key);')
|
// g.writeln('string_free(&$key);')
|
||||||
}
|
}
|
||||||
|
if it.label.len > 0 {
|
||||||
|
g.writeln('\t$it.label\__continue: {}')
|
||||||
|
}
|
||||||
g.writeln('}')
|
g.writeln('}')
|
||||||
|
if it.label.len > 0 {
|
||||||
|
g.writeln('\t$it.label\__break: {}')
|
||||||
|
}
|
||||||
g.writeln('/*for in map cleanup*/')
|
g.writeln('/*for in map cleanup*/')
|
||||||
g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; ++$idx) { string_free(&(($key_styp*)${keys_tmp}.data)[$idx]); }')
|
g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; ++$idx) { string_free(&(($key_styp*)${keys_tmp}.data)[$idx]); }')
|
||||||
g.writeln('array_free(&$keys_tmp);')
|
g.writeln('array_free(&$keys_tmp);')
|
||||||
|
return
|
||||||
} else if it.cond_type.has_flag(.variadic) {
|
} else if it.cond_type.has_flag(.variadic) {
|
||||||
g.writeln('// FOR IN cond_type/variadic')
|
g.writeln('// FOR IN cond_type/variadic')
|
||||||
i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
|
i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
|
||||||
|
@ -1215,8 +1245,6 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
g.write('\t$styp ${c_name(it.val_var)} = ')
|
g.write('\t$styp ${c_name(it.val_var)} = ')
|
||||||
g.expr(it.cond)
|
g.expr(it.cond)
|
||||||
g.writeln('.args[$i];')
|
g.writeln('.args[$i];')
|
||||||
g.stmts(it.stmts)
|
|
||||||
g.writeln('}')
|
|
||||||
} else if it.kind == .string {
|
} else if it.kind == .string {
|
||||||
i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
|
i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
|
||||||
g.write('for (int $i = 0; $i < ')
|
g.write('for (int $i = 0; $i < ')
|
||||||
|
@ -1227,12 +1255,18 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
|
||||||
g.expr(it.cond)
|
g.expr(it.cond)
|
||||||
g.writeln('.str[$i];')
|
g.writeln('.str[$i];')
|
||||||
}
|
}
|
||||||
g.stmts(it.stmts)
|
|
||||||
g.writeln('}')
|
|
||||||
} else {
|
} else {
|
||||||
s := g.table.type_to_str(it.cond_type)
|
s := g.table.type_to_str(it.cond_type)
|
||||||
g.error('for in: unhandled symbol `$it.cond` of type `$s`', it.pos)
|
g.error('for in: unhandled symbol `$it.cond` of type `$s`', it.pos)
|
||||||
}
|
}
|
||||||
|
g.stmts(it.stmts)
|
||||||
|
if it.label.len > 0 {
|
||||||
|
g.writeln('\t$it.label\__continue: {}')
|
||||||
|
}
|
||||||
|
g.writeln('}')
|
||||||
|
if it.label.len > 0 {
|
||||||
|
g.writeln('\t$it.label\__break: {}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use instead of expr() when you need to cast to union sum type (can add other casts also)
|
// use instead of expr() when you need to cast to union sum type (can add other casts also)
|
||||||
|
|
|
@ -594,6 +594,26 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
||||||
spos := p.tok.position()
|
spos := p.tok.position()
|
||||||
name := p.check_name()
|
name := p.check_name()
|
||||||
p.next()
|
p.next()
|
||||||
|
if p.tok.kind == .key_for {
|
||||||
|
mut stmt := p.stmt(is_top_level)
|
||||||
|
match mut stmt {
|
||||||
|
ast.ForStmt {
|
||||||
|
stmt.label = name
|
||||||
|
return *stmt
|
||||||
|
}
|
||||||
|
ast.ForInStmt {
|
||||||
|
stmt.label = name
|
||||||
|
return *stmt
|
||||||
|
}
|
||||||
|
ast.ForCStmt {
|
||||||
|
stmt.label = name
|
||||||
|
return *stmt
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return ast.GotoLabel{
|
return ast.GotoLabel{
|
||||||
name: name
|
name: name
|
||||||
pos: spos.extend(p.tok.position())
|
pos: spos.extend(p.tok.position())
|
||||||
|
@ -630,9 +650,15 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
||||||
}
|
}
|
||||||
.key_continue, .key_break {
|
.key_continue, .key_break {
|
||||||
tok := p.tok
|
tok := p.tok
|
||||||
|
line := p.tok.line_nr
|
||||||
p.next()
|
p.next()
|
||||||
|
mut label := ''
|
||||||
|
if p.tok.line_nr == line && p.tok.kind == .name {
|
||||||
|
label = p.check_name()
|
||||||
|
}
|
||||||
return ast.BranchStmt{
|
return ast.BranchStmt{
|
||||||
kind: tok.kind
|
kind: tok.kind
|
||||||
|
label: label
|
||||||
pos: tok.position()
|
pos: tok.position()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
fn test_labelled_for() {
|
||||||
|
mut i := 4
|
||||||
|
goto L1
|
||||||
|
L1: for {
|
||||||
|
i++
|
||||||
|
for {
|
||||||
|
if i < 7 {continue L1}
|
||||||
|
else {break L1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert i == 7
|
||||||
|
|
||||||
|
goto L2
|
||||||
|
L2: for ;; i++ {
|
||||||
|
for {
|
||||||
|
if i < 17 {continue L2}
|
||||||
|
else {break L2}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert i == 17
|
||||||
|
|
||||||
|
goto L3
|
||||||
|
L3: for e in [1,2,3,4] {
|
||||||
|
i = e
|
||||||
|
for {
|
||||||
|
if i < 3 {continue L3}
|
||||||
|
else {break L3}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert i == 3
|
||||||
|
}
|
Loading…
Reference in New Issue