braceless one-statement for-loops

This commit is contained in:
2026-07-15 23:27:18 +02:00
parent 7de0b7f268
commit 3cc750b3b2
4 changed files with 107 additions and 22 deletions
+19 -13
View File
@@ -1993,20 +1993,18 @@ parse_block :: proc(parser: ^Parser) -> []ast.Stmt_Id {
return body[:]
}
// parse_branch_body parses an `if` then/else body: a braced block (possibly on
// a following line) or a single brace-less statement (milestone 16). For the
// then-branch (`validate_condition`), a brace-less body requires the condition
// to be parenthesized unless it is a function call; the `else` keyword and the
// unwrap `|...|` already delimit, so those positions skip the check.
parse_branch_body :: proc(parser: ^Parser, validate_condition: bool, condition: ast.Expr_Id, has_captures: bool) -> []ast.Stmt_Id {
// parse_control_body parses a braced block (possibly on a following line) or a
// single brace-less statement. A non-empty diagnostic enforces the shared
// parenthesized-or-call rule for the preceding condition or iterable.
parse_control_body :: proc(parser: ^Parser, header: ast.Expr_Id, diagnostic: string) -> []ast.Stmt_Id {
skip_newlines(parser)
if current(parser).kind == .Left_Brace {
return parse_block(parser)
}
if validate_condition && !has_captures && condition != ast.INVALID_EXPR && int(condition) < len(parser.module.exprs) {
expr := parser.module.exprs[condition]
if len(diagnostic) > 0 && header != ast.INVALID_EXPR && int(header) < len(parser.module.exprs) {
expr := parser.module.exprs[header]
if !expr.parenthesized && expr.kind != .Call {
source.add(parser.diagnostics, expr.span, "a brace-less 'if' body requires the condition to be parenthesized unless it is a function call")
source.add(parser.diagnostics, expr.span, diagnostic)
}
}
single := make([]ast.Stmt_Id, 1, parser.module.allocator)
@@ -2056,7 +2054,11 @@ parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
source.add(parser.diagnostics, current(parser).span, "expected '|' to close unwrap captures")
}
}
then_body := parse_branch_body(parser, true, condition, len(captures) > 0)
then_body := parse_control_body(
parser,
condition,
"a brace-less 'if' body requires the condition to be parenthesized unless it is a function call",
)
else_body: []ast.Stmt_Id = nil
saved_cursor := parser.cursor
skip_newlines(parser)
@@ -2069,7 +2071,7 @@ parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
single[0] = nested
else_body = single
} else {
else_body = parse_branch_body(parser, false, ast.INVALID_EXPR, false)
else_body = parse_control_body(parser, ast.INVALID_EXPR, "")
}
} else {
parser.cursor = saved_cursor
@@ -2089,7 +2091,7 @@ parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
}
// parse_arm_body parses a match arm's body after the `:`: a braced block (whose
// inner statements are returned unwrapped, like `parse_branch_body`) or a single
// inner statements are returned unwrapped, like `parse_control_body`) or a single
// brace-less statement. For value-match a brace-less body is a single expression
// that the checker yields implicitly.
parse_arm_body :: proc(parser: ^Parser) -> []ast.Stmt_Id {
@@ -2306,7 +2308,11 @@ parse_for :: proc(parser: ^Parser) -> ast.Stmt_Id {
}
skip_newlines(parser)
label := parse_optional_loop_label(parser)
body := parse_block(parser)
body := parse_control_body(
parser,
iterable,
"a brace-less 'for' body requires the iterable to be parenthesized unless it is a function call",
)
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{