match statements
This commit is contained in:
+109
-1
@@ -1128,6 +1128,7 @@ parse_value_control_flow :: proc(parser: ^Parser) -> (ast.Stmt_Id, bool) {
|
||||
case .Keyword_If: return parse_if(parser), true
|
||||
case .Keyword_For: return parse_for(parser), true
|
||||
case .Keyword_While: return parse_while(parser), true
|
||||
case .Keyword_Match: return parse_match(parser), true
|
||||
}
|
||||
return ast.INVALID_STMT, false
|
||||
}
|
||||
@@ -1157,6 +1158,9 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
if current(parser).kind == .Keyword_Yield {
|
||||
return parse_yield(parser)
|
||||
}
|
||||
if current(parser).kind == .Keyword_Match {
|
||||
return parse_match(parser)
|
||||
}
|
||||
// A leading `{` opens a bare block scope (struct literals are postfix only).
|
||||
if current(parser).kind == .Left_Brace {
|
||||
return parse_block_statement(parser)
|
||||
@@ -1526,6 +1530,110 @@ parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
return 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
|
||||
// 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 {
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind == .Left_Brace {
|
||||
return parse_block(parser)
|
||||
}
|
||||
single := make([]ast.Stmt_Id, 1, parser.module.allocator)
|
||||
single[0] = parse_statement(parser)
|
||||
return single
|
||||
}
|
||||
|
||||
// parse_match_arm parses one `<pattern> [|capture|]: <body>` arm (or `else: <body>`).
|
||||
// The pattern is `INVALID_EXPR` for `else`; `captures` holds the optional 0-or-1
|
||||
// payload capture name (tagged-union variants only).
|
||||
parse_match_arm :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := current(parser).span
|
||||
pattern := ast.INVALID_EXPR
|
||||
captures: [dynamic]symbol.Id
|
||||
captures.allocator = parser.module.allocator
|
||||
if _, is_else := allow(parser, .Keyword_Else); !is_else {
|
||||
saved := parser.no_struct_literal
|
||||
parser.no_struct_literal = true
|
||||
pattern = parse_expression(parser)
|
||||
parser.no_struct_literal = saved
|
||||
if _, ok := allow(parser, .Pipe); ok {
|
||||
name_tok := current(parser)
|
||||
if name_tok.kind == .Identifier || name_tok.kind == .Underscore {
|
||||
advance(parser)
|
||||
append(&captures, name_tok.symbol)
|
||||
} else {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a capture name after '|'")
|
||||
}
|
||||
if _, close_ok := allow(parser, .Pipe); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '|' to close the match capture")
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, ok := allow(parser, .Colon); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ':' after a match pattern")
|
||||
}
|
||||
body := parse_arm_body(parser)
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Match_Arm,
|
||||
span=span_from(start, previous(parser).span),
|
||||
expr=pattern,
|
||||
captures=captures[:],
|
||||
body=body,
|
||||
target=ast.INVALID_EXPR,
|
||||
update=ast.INVALID_STMT,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
|
||||
// parse_match parses `match <subject> { <arm>* }`. Arms are newline-separated; each
|
||||
// is a `Match_Arm` statement stored in the `Match`'s `body`. Usable as a statement
|
||||
// and (via `parse_value_control_flow`) as a value source on a declaration/assignment.
|
||||
parse_match :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := advance(parser) // consume 'match'
|
||||
skip_newlines(parser)
|
||||
saved := parser.no_struct_literal
|
||||
parser.no_struct_literal = true
|
||||
subject := parse_expression(parser)
|
||||
parser.no_struct_literal = saved
|
||||
arms: [dynamic]ast.Stmt_Id
|
||||
arms.allocator = parser.module.allocator
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '{' to open match arms")
|
||||
}
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
append(&arms, parse_match_arm(parser))
|
||||
if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
arm_id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Invalid,
|
||||
span=current(parser).span,
|
||||
expr=ast.INVALID_EXPR,
|
||||
diagnostic=diagnostic,
|
||||
})
|
||||
append(&arms, arm_id)
|
||||
}
|
||||
}
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' to close match arms")
|
||||
}
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.Match,
|
||||
span=span_from(start.span, previous(parser).span),
|
||||
expr=subject,
|
||||
body=arms[:],
|
||||
target=ast.INVALID_EXPR,
|
||||
update=ast.INVALID_STMT,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
|
||||
parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
parenthesized := false
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
@@ -1559,7 +1667,7 @@ parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
statement := &parser.module.statements[update]
|
||||
switch statement.kind {
|
||||
case .Assignment, .Expression:
|
||||
case .Invalid, .Declaration, .Return, .If, .While, .For, .Break, .Continue, .Block, .Defer, .Yield:
|
||||
case .Invalid, .Declaration, .Return, .If, .While, .For, .Break, .Continue, .Block, .Defer, .Yield, .Match, .Match_Arm:
|
||||
diagnostic := source.add(
|
||||
parser.diagnostics,
|
||||
statement.span,
|
||||
|
||||
Reference in New Issue
Block a user