gen: implement labelled break and continue (#6880)
							parent
							
								
									41ba942369
								
							
						
					
					
						commit
						e798326a1a
					
				|  | @ -284,8 +284,9 @@ pub mut: | |||
| // break, continue
 | ||||
| pub struct BranchStmt { | ||||
| pub: | ||||
| 	kind token.Kind | ||||
| 	pos  token.Position | ||||
| 	kind  token.Kind | ||||
| 	label string | ||||
| 	pos   token.Position | ||||
| } | ||||
| 
 | ||||
| pub struct CallExpr { | ||||
|  | @ -626,6 +627,8 @@ pub: | |||
| 	stmts  []Stmt | ||||
| 	is_inf bool // `for {}`
 | ||||
| 	pos    token.Position | ||||
| pub mut: | ||||
| 	label  string // `label: for {`
 | ||||
| } | ||||
| 
 | ||||
| pub struct ForInStmt { | ||||
|  | @ -644,6 +647,7 @@ pub mut: | |||
| 	val_type   table.Type | ||||
| 	cond_type  table.Type | ||||
| 	kind       table.Kind // array/map/string
 | ||||
| 	label      string // `label: for {`
 | ||||
| } | ||||
| 
 | ||||
| pub struct ForCStmt { | ||||
|  | @ -656,6 +660,8 @@ pub: | |||
| 	has_inc  bool | ||||
| 	stmts    []Stmt | ||||
| 	pos      token.Position | ||||
| pub mut: | ||||
| 	label    string // `label: for {`
 | ||||
| } | ||||
| 
 | ||||
| // #include etc
 | ||||
|  |  | |||
|  | @ -2410,6 +2410,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { | |||
| 			if c.in_for_count == 0 { | ||||
| 				c.error('$node.kind.str() statement not within a loop', node.pos) | ||||
| 			} | ||||
| 			// TODO: check any node.label is in scope for goto
 | ||||
| 		} | ||||
| 		ast.CompFor { | ||||
| 			// node.typ = c.expr(node.expr)
 | ||||
|  | @ -2577,7 +2578,9 @@ fn (mut c Checker) stmt(node ast.Stmt) { | |||
| 			} | ||||
| 		} | ||||
| 		ast.GotoLabel {} | ||||
| 		ast.GotoStmt {} | ||||
| 		ast.GotoStmt { | ||||
| 			// TODO: check label doesn't bypass variable declarations
 | ||||
| 		} | ||||
| 		ast.HashStmt { | ||||
| 			c.hash_stmt(mut node) | ||||
| 		} | ||||
|  |  | |||
|  | @ -821,9 +821,17 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 		} | ||||
| 		ast.BranchStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			// continue or break
 | ||||
| 			g.write(node.kind.str()) | ||||
| 			g.writeln(';') | ||||
| 			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
 | ||||
| 				g.writeln('$node.kind;') | ||||
| 			} | ||||
| 		} | ||||
| 		ast.ConstDecl { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
|  | @ -930,6 +938,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 		ast.ForCStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.is_vlines_enabled = false | ||||
| 			if node.label.len > 0 { | ||||
| 				g.writeln('$node.label:') | ||||
| 			} | ||||
| 			g.write('for (') | ||||
| 			if !node.has_init { | ||||
| 				g.write('; ') | ||||
|  | @ -952,7 +963,13 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 			g.writeln(') {') | ||||
| 			g.is_vlines_enabled = true | ||||
| 			g.stmts(node.stmts) | ||||
| 			if node.label.len > 0 { | ||||
| 				g.writeln('$node.label\__continue: {}') | ||||
| 			} | ||||
| 			g.writeln('}') | ||||
| 			if node.label.len > 0 { | ||||
| 				g.writeln('$node.label\__break: {}') | ||||
| 			} | ||||
| 		} | ||||
| 		ast.ForInStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
|  | @ -961,6 +978,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 		ast.ForStmt { | ||||
| 			g.write_v_source_line_info(node.pos) | ||||
| 			g.is_vlines_enabled = false | ||||
| 			if node.label.len > 0 { | ||||
| 				g.writeln('$node.label:') | ||||
| 			} | ||||
| 			g.writeln('for (;;) {') | ||||
| 			if !node.is_inf { | ||||
| 				g.indent++ | ||||
|  | @ -972,7 +992,13 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 			} | ||||
| 			g.is_vlines_enabled = true | ||||
| 			g.stmts(node.stmts) | ||||
| 			if node.label.len > 0 { | ||||
| 				g.writeln('\t$node.label\__continue: {}') | ||||
| 			} | ||||
| 			g.writeln('}') | ||||
| 			if node.label.len > 0 { | ||||
| 				g.writeln('$node.label\__break: {}') | ||||
| 			} | ||||
| 		} | ||||
| 		ast.GlobalDecl { | ||||
| 			g.global_decl(node) | ||||
|  | @ -1097,6 +1123,9 @@ fn (mut g Gen) write_defer_stmts() { | |||
| } | ||||
| 
 | ||||
| fn (mut g Gen) for_in(it ast.ForInStmt) { | ||||
| 	if it.label.len > 0 { | ||||
| 		g.writeln('\t$it.label: {}') | ||||
| 	} | ||||
| 	if it.is_range { | ||||
| 		// `for x in 1..10 {`
 | ||||
| 		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.expr(it.high) | ||||
| 		g.writeln('; ++$i) {') | ||||
| 		g.stmts(it.stmts) | ||||
| 		g.writeln('}') | ||||
| 	} else if it.kind == .array { | ||||
| 		// `for num in nums {`
 | ||||
| 		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.stmts(it.stmts) | ||||
| 		g.writeln('}') | ||||
| 	} else if it.kind == .array_fixed { | ||||
| 		atmp := g.new_tmp_var() | ||||
| 		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.stmts(it.stmts) | ||||
| 		g.writeln('}') | ||||
| 	} else if it.kind == .map { | ||||
| 		// `for key, val 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 { | ||||
| 			// g.writeln('string_free(&$key);')
 | ||||
| 		} | ||||
| 		if it.label.len > 0 { | ||||
| 			g.writeln('\t$it.label\__continue: {}') | ||||
| 		} | ||||
| 		g.writeln('}') | ||||
| 		if it.label.len > 0 { | ||||
| 			g.writeln('\t$it.label\__break: {}') | ||||
| 		} | ||||
| 		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('array_free(&$keys_tmp);') | ||||
| 		return | ||||
| 	} else if it.cond_type.has_flag(.variadic) { | ||||
| 		g.writeln('// FOR IN cond_type/variadic') | ||||
| 		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.expr(it.cond) | ||||
| 		g.writeln('.args[$i];') | ||||
| 		g.stmts(it.stmts) | ||||
| 		g.writeln('}') | ||||
| 	} else if it.kind == .string { | ||||
| 		i := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var } | ||||
| 		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.writeln('.str[$i];') | ||||
| 		} | ||||
| 		g.stmts(it.stmts) | ||||
| 		g.writeln('}') | ||||
| 	} else { | ||||
| 		s := g.table.type_to_str(it.cond_type) | ||||
| 		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)
 | ||||
|  |  | |||
|  | @ -594,6 +594,26 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { | |||
| 					spos := p.tok.position() | ||||
| 					name := p.check_name() | ||||
| 					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{ | ||||
| 						name: name | ||||
| 						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 { | ||||
| 			tok := p.tok | ||||
| 			line := p.tok.line_nr | ||||
| 			p.next() | ||||
| 			mut label := '' | ||||
| 			if p.tok.line_nr == line && p.tok.kind == .name { | ||||
| 				label = p.check_name() | ||||
| 			} | ||||
| 			return ast.BranchStmt{ | ||||
| 				kind: tok.kind | ||||
| 				label: label | ||||
| 				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