feat: expand constant inline for loops
This commit is contained in:
+13
-8
@@ -293,7 +293,7 @@ parse_statement proc(state @mut State, allow_else bool) ast.NodeId ! ParseError
|
||||
match current(state).kind {
|
||||
.if: return try parse_if(state)
|
||||
.while: return try parse_while(state, allow_else)
|
||||
.for: return try parse_for(state, allow_else)
|
||||
.for, .inline: return try parse_for(state, allow_else)
|
||||
.return: return try parse_return(state, allow_else)
|
||||
.break, .continue: return try parse_loop_control(state, allow_else)
|
||||
.defer, .errdefer: return try parse_cleanup(state, allow_else)
|
||||
@@ -510,7 +510,10 @@ parse_captures proc(state @mut State) CaptureList ! ParseError {
|
||||
|
||||
@hide:file
|
||||
parse_for proc(state @mut State, allow_else bool) ast.NodeId ! ParseError {
|
||||
keyword :: try consume(state, .for)
|
||||
keyword :: current(state)
|
||||
inlined :: next_is(state, .inline)
|
||||
if (inlined) state.next_token += 1
|
||||
_ = try consume(state, .for)
|
||||
skip_newlines(state)
|
||||
iterable_start :: state.next_token
|
||||
iterable := try parse_guard_expr(state)
|
||||
@@ -556,7 +559,7 @@ parse_for proc(state @mut State, allow_else bool) ast.NodeId ! ParseError {
|
||||
and !next_is(state, .open_curly)
|
||||
) state.next_token = after_captures
|
||||
body :: try parse_arm(state, allow_else)
|
||||
return try ast.add_for_stmt(
|
||||
id :: try ast.add_for_stmt(
|
||||
state.builder,
|
||||
ast.Span{
|
||||
start = keyword.span.start,
|
||||
@@ -565,6 +568,8 @@ parse_for proc(state @mut State, allow_else bool) ast.NodeId ! ParseError {
|
||||
},
|
||||
iterable, captures.item, captures.index, captures.pointer, captures.indexed, body,
|
||||
)
|
||||
if (inlined) ast.set_for_inline(state.builder, id)
|
||||
return id
|
||||
}
|
||||
|
||||
@hide:file
|
||||
@@ -599,7 +604,7 @@ parse_guard proc(state @mut State) ast.NodeId ! ParseError {
|
||||
and !next_is(state, .newline) and !next_is(state, .eof)
|
||||
and !next_is(state, .else) and !next_is(state, .return)
|
||||
and !next_is(state, .if) and !next_is(state, .while)
|
||||
and !next_is(state, .for)
|
||||
and !next_is(state, .for) and !next_is(state, .inline)
|
||||
and !next_is(state, .break) and !next_is(state, .continue)
|
||||
) {
|
||||
if (next_is(state, .ident) and next_n_is(state, 1, .equal)) break
|
||||
@@ -850,7 +855,7 @@ starts_type proc(kind lexer.TokenKind) bool {
|
||||
@hide:file
|
||||
member_name proc(kind lexer.TokenKind) bool {
|
||||
return match kind {
|
||||
.ident, .if, .else, .for, .while, .proc, .return, .break, .continue,
|
||||
.ident, .if, .else, .for, .inline, .while, .proc, .return, .break, .continue,
|
||||
.true, .false, .and, .or, .mut, .struct, .enum, .union, .distinct,
|
||||
.alias, .match, .import, .test, .c_func, .null, .undefined, .try, .catch, .defer, .errdefer: true
|
||||
else: false
|
||||
@@ -1071,7 +1076,7 @@ parse_match_arm proc(state @mut State) ast.NodeId ! ParseError {
|
||||
state.match_arm_body = true
|
||||
defer state.match_arm_body = previous
|
||||
statement_body :: match current(state).kind {
|
||||
.open_curly, .return, .break, .continue, .if, .while, .for, .defer, .errdefer: true
|
||||
.open_curly, .return, .break, .continue, .if, .while, .for, .inline, .defer, .errdefer: true
|
||||
else: false
|
||||
}
|
||||
if (statement_body or named_binding_starts(state)) {
|
||||
@@ -1703,7 +1708,7 @@ parse_primary proc(state @mut State) ast.NodeId ! ParseError {
|
||||
@hide:file
|
||||
is_expression_boundary proc(kind lexer.TokenKind) bool {
|
||||
return match kind {
|
||||
.close_paren, .newline, .close_curly, .open_curly, .else, .return, .if, .while, .for, .defer, .errdefer, .eof: true
|
||||
.close_paren, .newline, .close_curly, .open_curly, .else, .return, .if, .while, .for, .inline, .defer, .errdefer, .eof: true
|
||||
else: false
|
||||
}
|
||||
}
|
||||
@@ -1842,7 +1847,7 @@ recovery_closes_delimiters proc(state @State, paren_depth, square_depth usize) b
|
||||
for (state.next_token)..(state.tokens.len) |i| {
|
||||
kind :: state.tokens[i].kind
|
||||
match kind {
|
||||
.open_curly, .close_curly, .eof, .if, .else, .while, .for,
|
||||
.open_curly, .close_curly, .eof, .if, .else, .while, .for, .inline,
|
||||
.return, .break, .continue, .defer, .errdefer: return false
|
||||
.ident: {
|
||||
if (i + 1 < state.tokens.len) {
|
||||
|
||||
@@ -1232,6 +1232,114 @@ parses_range_endpoints_and_capture_shapes test {
|
||||
try testing.expect_equal(ast.name_id(&tree, fourth.index?), ast.name_id(&tree, index_reference))
|
||||
}
|
||||
|
||||
parses_inline_loops_and_clones_capture_metadata 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() void {
|
||||
` inline for 0..=2 |item| {
|
||||
` for "ab" |byte, index| { continue }
|
||||
` }
|
||||
` for [1, 2] |value, index| {}
|
||||
` inline for [i32, u8] |@T, _| {}
|
||||
` inline for ("xy") |byte, index| continue
|
||||
`}
|
||||
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
|
||||
try testing.expect_equal(4, ast.block_statement_count(&tree, body))
|
||||
first :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 0))
|
||||
try testing.expect(first.is_inline and !first.pointer_capture and !first.index_capture)
|
||||
try testing.expect(ast.range_expr(&tree, first.iterable).inclusive)
|
||||
try testing.expect_equal("inline", ast.source_text(&tree, ast.Span{
|
||||
start = first.span.start, end = first.span.start + 6, source = first.span.source,
|
||||
}))
|
||||
nested :: ast.for_stmt(&tree, ast.block_statement(&tree, first.body, 0))
|
||||
try testing.expect(!nested.is_inline and nested.index_capture)
|
||||
try testing.expect_equal(ast.ExprKind.literal_string, ast.expr_kind(&tree, nested.iterable))
|
||||
ordinary :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 1))
|
||||
try testing.expect(!ordinary.is_inline and ordinary.index_capture)
|
||||
try testing.expect_equal(2, ast.array_element_count(&tree, ordinary.iterable))
|
||||
pointer :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 2))
|
||||
try testing.expect(pointer.is_inline and pointer.pointer_capture and pointer.index_capture)
|
||||
try testing.expect(pointer.index == null)
|
||||
try testing.expect_equal("T", strpool.get_str(&strings, ast.name_id(&tree, pointer.capture?))?)
|
||||
indexed :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3))
|
||||
try testing.expect(indexed.is_inline and !indexed.pointer_capture and indexed.index_capture)
|
||||
try testing.expect_equal("index", strpool.get_str(&strings, ast.name_id(&tree, indexed.index?))?)
|
||||
try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(
|
||||
&tree, ast.block_statement(&tree, indexed.body, 0),
|
||||
))
|
||||
}
|
||||
original_body :: ast.function_decl(&tree, ast.child(&tree, root, 0)).body
|
||||
cloned_body :: ast.function_decl(&tree, ast.child(&tree, cloned, 0)).body
|
||||
first :: ast.for_stmt(&tree, ast.block_statement(&tree, original_body, 0))
|
||||
copy :: ast.for_stmt(&tree, ast.block_statement(&tree, cloned_body, 0))
|
||||
try testing.expect(copy.capture? != first.capture?)
|
||||
try testing.expect_equal(mapping[usize(first.capture?)], copy.capture?)
|
||||
try testing.expect_equal(first.span.start, copy.span.start)
|
||||
try testing.expect_equal(first.span.end, copy.span.end)
|
||||
try testing.expect_equal(first.span.source, copy.span.source)
|
||||
}
|
||||
|
||||
rejects_malformed_inline_forms_without_losing_siblings test {
|
||||
strings := strpool.init(mem.c_allocator)
|
||||
defer strpool.deinit(&strings)
|
||||
cases [7][]u8 :: [
|
||||
"inline while true {}",
|
||||
"inline match value { else: {} }",
|
||||
"inline 0..2 |item| {}",
|
||||
"inline inline for 0..2 |item| {}",
|
||||
"inline",
|
||||
"inline for 0..2 || {}",
|
||||
"inline for 0..2 |item,| {}",
|
||||
]
|
||||
for (cases) |header| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
source arraylist.ArrayList(u8) := arraylist.init(mem.c_allocator)
|
||||
defer arraylist.deinit(&source)
|
||||
for ("run proc() void {\n") |byte| try arraylist.append(&source, byte)
|
||||
for (header) |byte| try arraylist.append(&source, byte)
|
||||
for ("\ninline for (0..2) |item| continue\nkept :: 7\n}\nafter :: 9\n") |byte|
|
||||
try arraylist.append(&source, byte)
|
||||
scan_state := lexer.init(mem.c_allocator)
|
||||
defer lexer.deinit(&scan_state)
|
||||
try lexer.scan(&scan_state, source.items, &strings, &diagnostic_store)
|
||||
tree := try parse(mem.c_allocator, scan_state.tokens.items, source.items, &diagnostic_store)
|
||||
defer ast.deinit(&tree)
|
||||
try testing.expect(diagnostics.count(&diagnostic_store) > 0)
|
||||
try testing.expect_equal(2, ast.package_decl_count(&tree))
|
||||
body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body
|
||||
try testing.expect_equal(3, ast.block_statement_count(&tree, body))
|
||||
preserved :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 1))
|
||||
try testing.expect(preserved.is_inline)
|
||||
try testing.expect_equal(ast.StmtKind.continue_stmt, ast.stmt_kind(
|
||||
&tree, ast.block_statement(&tree, preserved.body, 0),
|
||||
))
|
||||
kept :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 2))
|
||||
try testing.expect_equal("kept", ast.source_text(&tree, kept.name))
|
||||
try testing.expect_equal("after", ast.source_text(
|
||||
&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 1)).name,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
rejects_bro_invalid_range_headers_without_losing_siblings test {
|
||||
strings := strpool.init(mem.c_allocator)
|
||||
defer strpool.deinit(&strings)
|
||||
|
||||
Reference in New Issue
Block a user