gen: implement labelled break and continue (#6880)

pull/6882/head
Nick Treleaven 2020-11-19 20:13:15 +00:00 committed by GitHub
parent 41ba942369
commit e798326a1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 116 additions and 16 deletions

View File

@ -284,8 +284,9 @@ pub mut:
// break, continue // break, continue
pub struct BranchStmt { pub struct BranchStmt {
pub: pub:
kind token.Kind kind token.Kind
pos token.Position label string
pos token.Position
} }
pub struct CallExpr { pub struct CallExpr {
@ -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

View File

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

View File

@ -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)
// continue or break if node.label.len > 0 {
g.write(node.kind.str()) if node.kind == .key_break {
g.writeln(';') g.writeln('goto $node.label\__break;')
} else {
assert node.kind == .key_continue
g.writeln('goto $node.label\__continue;')
}
} else {
// continue or break
g.writeln('$node.kind;')
}
} }
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)

View File

@ -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()
} }
} }

View File

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