feat: expand nominal inline match arms
This commit is contained in:
+51
-26
@@ -1048,27 +1048,39 @@ parse_match_arm proc(state @mut State) ast.NodeId ! ParseError {
|
||||
start :: current(state).span
|
||||
patterns arraylist.ArrayList(ast.NodeId) := arraylist.init(state.allocator)
|
||||
defer arraylist.deinit(&patterns)
|
||||
catch_all :: next_is(state, .else)
|
||||
is_inline :: next_is(state, .inline)
|
||||
if (is_inline) state.next_token += 1
|
||||
catch_all :: !is_inline and next_is(state, .else)
|
||||
capture ?ast.NodeId := null
|
||||
tag_capture ?ast.NodeId := null
|
||||
tag_capture_present := false
|
||||
if (catch_all) state.next_token += 1
|
||||
else {
|
||||
else if (!is_inline) {
|
||||
while true {
|
||||
try arraylist.append(&patterns, try parse_guard_expr(state))
|
||||
if (!next_is(state, .comma)) break
|
||||
state.next_token += 1
|
||||
skip_newlines(state)
|
||||
}
|
||||
if next_is(state, .pipe) {
|
||||
}
|
||||
if (is_inline or (!catch_all and next_is(state, .pipe))) {
|
||||
_ = try consume(state, .pipe)
|
||||
# captures are by value; pointer capture tokens are not identifiers.
|
||||
token :: try consume(state, .ident)
|
||||
if (
|
||||
token.span.end != token.span.start + 1
|
||||
or state.builder.source[usize(token.span.start)] != '_'
|
||||
) capture = try ast.add_identifier(state.builder, token.span, token.data.str_id)
|
||||
if (is_inline and next_is(state, .comma)) {
|
||||
state.next_token += 1
|
||||
# pointer payload captures are outside the current runtime representation.
|
||||
if (next_is(state, .at) or next_is(state, .star)) return .unexpected_token
|
||||
token :: try consume(state, .ident)
|
||||
tag_capture_present = true
|
||||
tag :: try consume(state, .ident)
|
||||
if (
|
||||
token.span.end != token.span.start + 1
|
||||
or state.builder.source[usize(token.span.start)] != '_'
|
||||
) capture = try ast.add_identifier(state.builder, token.span, token.data.str_id)
|
||||
_ = try consume(state, .pipe)
|
||||
tag.span.end != tag.span.start + 1
|
||||
or state.builder.source[usize(tag.span.start)] != '_'
|
||||
) tag_capture = try ast.add_identifier(state.builder, tag.span, tag.data.str_id)
|
||||
}
|
||||
_ = try consume(state, .pipe)
|
||||
}
|
||||
_ = try consume(state, .colon)
|
||||
skip_newlines(state)
|
||||
@@ -1079,24 +1091,37 @@ parse_match_arm proc(state @mut State) ast.NodeId ! ParseError {
|
||||
.open_curly, .return, .break, .continue, .if, .while, .for, .inline, .defer, .errdefer: true
|
||||
else: false
|
||||
}
|
||||
body ast.NodeId := undefined
|
||||
expression := false
|
||||
if (statement_body or named_binding_starts(state)) {
|
||||
body :: try parse_arm(state, false)
|
||||
return try ast.add_match_arm(state.builder, ast.Span{
|
||||
start = start.start, end = ast.builder_span(state.builder, body).end, source = start.source,
|
||||
}, patterns.items, capture, body, false, catch_all)
|
||||
body = try parse_arm(state, false)
|
||||
} else {
|
||||
value :: try parse_expr(state)
|
||||
if next_is(state, .equal) {
|
||||
statement :: try parse_assignment(state, value, false)
|
||||
statements [1]ast.NodeId :: [statement]
|
||||
body = try ast.add_block(state.builder, ast.builder_span(state.builder, statement), statements[..])
|
||||
} else {
|
||||
body = value
|
||||
expression = true
|
||||
}
|
||||
tree :: ast.view(state.builder, value)
|
||||
if (expression and ast.expr_kind(&tree, value) == .match_expr
|
||||
and ast.match_arm_count(&tree, value) > 0) {
|
||||
nested :: ast.match_arm_view(&tree, ast.match_arm(&tree, value, 0))
|
||||
if (!nested.expression) {
|
||||
statement :: try ast.add_expression_stmt(state.builder, value)
|
||||
statements [1]ast.NodeId :: [statement]
|
||||
body = try ast.add_block(state.builder, ast.builder_span(state.builder, value), statements[..])
|
||||
expression = false
|
||||
}
|
||||
}
|
||||
}
|
||||
value :: try parse_expr(state)
|
||||
if next_is(state, .equal) {
|
||||
statement :: try parse_assignment(state, value, false)
|
||||
statements [1]ast.NodeId :: [statement]
|
||||
body :: try ast.add_block(state.builder, ast.builder_span(state.builder, statement), statements[..])
|
||||
return try ast.add_match_arm(state.builder, ast.Span{
|
||||
start = start.start, end = ast.builder_span(state.builder, body).end, source = start.source,
|
||||
}, patterns.items, capture, body, false, catch_all)
|
||||
}
|
||||
return try ast.add_match_arm(state.builder, ast.Span{
|
||||
start = start.start, end = ast.builder_span(state.builder, value).end, source = start.source,
|
||||
}, patterns.items, capture, value, true, catch_all)
|
||||
arm :: try ast.add_match_arm(state.builder, ast.Span{
|
||||
start = start.start, end = ast.builder_span(state.builder, body).end, source = start.source,
|
||||
}, patterns.items, capture, body, expression, catch_all)
|
||||
if (is_inline) ast.set_match_arm_inline(state.builder, arm, tag_capture, tag_capture_present)
|
||||
return arm
|
||||
}
|
||||
|
||||
@hide:file
|
||||
|
||||
@@ -1899,3 +1899,79 @@ rejects_pointer_match_captures_and_preserves_siblings test {
|
||||
try testing.expect_equal("after", ast.source_text(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 1)).name))
|
||||
try testing.expect_equal("last", ast.source_text(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 3)).name))
|
||||
}
|
||||
|
||||
parses_and_clones_inline_match_captures_and_body_forms test {
|
||||
strings := strpool.init(mem.c_allocator)
|
||||
defer strpool.deinit(&strings)
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
source ::
|
||||
`run proc(value int) void {
|
||||
` match value { .if, .return: 1, inline |payload, tag|: payload }
|
||||
` match value { inline |_, _|: return }
|
||||
` match value { inline |_|: value = 1 }
|
||||
`}
|
||||
scan_state := lexer.init(mem.c_allocator)
|
||||
defer lexer.deinit(&scan_state)
|
||||
try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
|
||||
builder := ast.init_builder(mem.c_allocator, source)
|
||||
defer ast.deinit_builder(&builder)
|
||||
root :: try parse_source(&builder, scan_state.tokens.items, &diagnostic_store)
|
||||
original :: ast.view(&builder, root)
|
||||
mapping :: try mem.alloc(ast.NodeId, mem.c_allocator, ast.node_count(&original))
|
||||
defer mem.free(mem.c_allocator, mapping)
|
||||
mem.set(mapping, maxval!(ast.NodeId))
|
||||
cloned :: try ast.clone_subtree(&builder, root, mapping)
|
||||
tree :: ast.view(&builder, root)
|
||||
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
|
||||
roots [2]ast.NodeId :: [root, cloned]
|
||||
for (roots) |package| {
|
||||
body :: ast.function_decl(&tree, ast.child(&tree, package, 0)).body
|
||||
matched :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 0))
|
||||
ordinary :: ast.match_arm(&tree, matched, 0)
|
||||
try testing.expect_equal(2, ast.match_pattern_count(&tree, ordinary))
|
||||
try testing.expect(!ast.match_arm_view(&tree, ordinary).is_inline)
|
||||
arm_id :: ast.match_arm(&tree, matched, 1)
|
||||
arm :: ast.match_arm_view(&tree, arm_id)
|
||||
try testing.expect(arm.is_inline and arm.expression and arm.tag_capture_present)
|
||||
try testing.expect_equal(0, ast.match_pattern_count(&tree, arm_id))
|
||||
try testing.expect_equal(3, ast.child_count(&tree, arm_id))
|
||||
try testing.expect_equal(arm.body, ast.child(&tree, arm_id, 0))
|
||||
try testing.expect_equal(arm.capture?, ast.child(&tree, arm_id, 1))
|
||||
try testing.expect_equal(arm.tag_capture?, ast.child(&tree, arm_id, 2))
|
||||
try testing.expect_equal("payload", ast.source_text(&tree, ast.node_span(&tree, arm.capture?)))
|
||||
try testing.expect_equal("tag", ast.source_text(&tree, ast.node_span(&tree, arm.tag_capture?)))
|
||||
for (usize(1)..usize(3)) |i| {
|
||||
statement_match :: ast.statement_expr(&tree, ast.block_statement(&tree, body, i))
|
||||
discarded :: ast.match_arm_view(&tree, ast.match_arm(&tree, statement_match, 0))
|
||||
try testing.expect(discarded.is_inline and !discarded.expression)
|
||||
try testing.expect(discarded.capture == null and discarded.tag_capture == null)
|
||||
try testing.expect_equal(i == 1, discarded.tag_capture_present)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rejects_malformed_inline_match_captures test {
|
||||
sources [8][]u8 :: [
|
||||
"run proc() void { match value { inline ||: 1 } }",
|
||||
"run proc() void { match value { inline |a, b, c|: 1 } }",
|
||||
"run proc() void { match value { inline |@a|: 1 } }",
|
||||
"run proc() void { match value { inline |*a|: 1 } }",
|
||||
"run proc() void { match value { inline |a, @b|: 1 } }",
|
||||
"run proc() void { match value { inline |a: 1 } }",
|
||||
"run proc() void { match value { inline a|: 1 } }",
|
||||
"run proc() void { match value { inline |a,|: 1 } }",
|
||||
]
|
||||
for (sources) |source| {
|
||||
strings := strpool.init(mem.c_allocator)
|
||||
defer strpool.deinit(&strings)
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
scan_state := lexer.init(mem.c_allocator)
|
||||
defer lexer.deinit(&scan_state)
|
||||
try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
|
||||
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
|
||||
defer ast.deinit(&tree)
|
||||
try testing.expect(diagnostics.count(&diagnostic_store) > 0)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user