Implement for
This commit is contained in:
parent
b24b5f29f7
commit
1213be0128
309
src/compiler.bbb
309
src/compiler.bbb
@ -2782,8 +2782,8 @@ StatementFor = struct
|
||||
{
|
||||
first_local: &Local,
|
||||
last_local: &Local,
|
||||
kinds: ValueKind,
|
||||
iteratables: &Value,
|
||||
kinds: []ValueKind,
|
||||
iteratables: []&Value,
|
||||
predicate: &Statement,
|
||||
scope: Scope,
|
||||
kind: StatementForKind,
|
||||
@ -2811,6 +2811,12 @@ Statement = struct
|
||||
id: StatementId,
|
||||
}
|
||||
|
||||
scope_to_for = fn (scope: &Scope) &StatementFor
|
||||
{
|
||||
assert(scope.kind == .for_each);
|
||||
return #field_parent_pointer(scope, "scope");
|
||||
}
|
||||
|
||||
scope_to_block = fn (scope: &Scope) &Block
|
||||
{
|
||||
assert(scope.kind == .local);
|
||||
@ -2852,7 +2858,17 @@ new_local = fn (module: &Module, scope: &Scope) &Local
|
||||
},
|
||||
.for_each =>
|
||||
{
|
||||
#trap();
|
||||
>for_each = scope_to_for(scope);
|
||||
if (for_each.last_local)
|
||||
{
|
||||
for_each.last_local.next = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
for_each.first_local = result;
|
||||
}
|
||||
|
||||
for_each.last_local = result;
|
||||
}
|
||||
else =>
|
||||
{
|
||||
@ -4918,7 +4934,20 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [
|
||||
},
|
||||
.for_each =>
|
||||
{
|
||||
#trap();
|
||||
assert(scope.parent != zero);
|
||||
>for_each = scope_to_for(scope);
|
||||
|
||||
>local = for_each.first_local;
|
||||
while (local)
|
||||
{
|
||||
if (string_equal(identifier, local.variable.name))
|
||||
{
|
||||
variable = &local.variable;
|
||||
break;
|
||||
}
|
||||
|
||||
local = local.next;
|
||||
}
|
||||
},
|
||||
.macro_declaration =>
|
||||
{
|
||||
@ -6030,7 +6059,137 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement
|
||||
},
|
||||
.for =>
|
||||
{
|
||||
#trap();
|
||||
skip_space(module);
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
|
||||
>parent_scope = scope;
|
||||
|
||||
statement.content = {
|
||||
.for = {
|
||||
.scope = {
|
||||
.parent = parent_scope,
|
||||
.line = statement_line,
|
||||
.column = statement_column,
|
||||
.kind = .for_each,
|
||||
zero,
|
||||
},
|
||||
zero,
|
||||
},
|
||||
};
|
||||
statement.id = .for;
|
||||
|
||||
>scope = &statement.content.for.scope;
|
||||
>left_value_buffer: [64]ValueKind = undefined;
|
||||
>left_value_count: u64 = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
>is_left = module.content[module.offset] == '&';
|
||||
module.offset += #extend(is_left);
|
||||
|
||||
>for_local_line = get_line(module);
|
||||
>for_local_column = get_column(module);
|
||||
|
||||
if (is_identifier_start(module.content[module.offset]))
|
||||
{
|
||||
>local_name = arena_duplicate_string(module.arena, parse_identifier(module));
|
||||
>local = new_local(module, scope);
|
||||
local.& = {
|
||||
.variable = {
|
||||
.scope = scope,
|
||||
.name = local_name,
|
||||
.line = for_local_line,
|
||||
.column = for_local_column,
|
||||
zero,
|
||||
},
|
||||
zero,
|
||||
};
|
||||
|
||||
>kind: ValueKind = #select(is_left, .left, .right);
|
||||
|
||||
left_value_buffer[left_value_count] = kind;
|
||||
left_value_count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
if (!consume_character_if_match(module, ','))
|
||||
{
|
||||
expect_character(module, ':');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>right_value_buffer: [64]&Value = undefined;
|
||||
>right_value_count: u64 = 0;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
right_value_buffer[right_value_count] = parse_value(module, scope, zero);
|
||||
right_value_count += 1;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>token = tokenize(module);
|
||||
|
||||
>for_kind: StatementForKind = undefined;
|
||||
|
||||
switch (token.id)
|
||||
{
|
||||
.double_dot =>
|
||||
{
|
||||
if (left_value_count != 1)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
right_value_buffer[right_value_count] = parse_value(module, scope, zero);
|
||||
right_value_count += 1;
|
||||
|
||||
expect_character(module, right_parenthesis);
|
||||
for_kind = .range;
|
||||
},
|
||||
.right_parenthesis =>
|
||||
{
|
||||
right_value_buffer[0].kind = .left;
|
||||
for_kind = .slice;
|
||||
},
|
||||
else => { report_error(); },
|
||||
}
|
||||
|
||||
statement.content.for.kind = for_kind;
|
||||
|
||||
if (for_kind == .slice and left_value_count != right_value_count)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
>left_values = arena_allocate_slice[ValueKind](module.arena, left_value_count);
|
||||
memcpy(#pointer_cast(left_values.pointer), #pointer_cast(&left_value_buffer), left_value_count * #byte_size(ValueKind));
|
||||
|
||||
>right_values = arena_allocate_slice[&Value](module.arena, right_value_count);
|
||||
memcpy(#pointer_cast(right_values.pointer), #pointer_cast(&right_value_buffer), right_value_count * #byte_size(&Value));
|
||||
|
||||
statement.content.for.kinds = left_values;
|
||||
statement.content.for.iteratables = right_values;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>predicate = parse_statement(module, scope);
|
||||
statement.content.for.predicate = predicate;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
require_semicolon = 0;
|
||||
},
|
||||
.while =>
|
||||
{
|
||||
@ -13471,6 +13630,144 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v
|
||||
LLVMClearInsertionPosition(module.llvm.builder);
|
||||
}
|
||||
},
|
||||
.for =>
|
||||
{
|
||||
if (module.has_debug_info)
|
||||
{
|
||||
>lexical_block = LLVMDIBuilderCreateLexicalBlock(module.llvm.di_builder, statement.content.for.scope.parent.llvm, module.llvm.file, statement.content.for.scope.line, statement.content.for.scope.column);
|
||||
statement.content.for.scope.llvm = lexical_block;
|
||||
}
|
||||
|
||||
>index_type = uint64(module);
|
||||
resolve_type_in_place(module, index_type);
|
||||
>index_zero = LLVMConstNull(index_type.llvm.abi);
|
||||
|
||||
>entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.entry");
|
||||
>body_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.body");
|
||||
>continue_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.continue");
|
||||
>exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.exit");
|
||||
|
||||
>previous_continue_block = module.llvm.continue_block;
|
||||
>previous_exit_block = module.llvm.exit_block;
|
||||
|
||||
module.llvm.continue_block = continue_block;
|
||||
module.llvm.exit_block = exit_block;
|
||||
|
||||
>kinds = statement.content.for.kinds;
|
||||
>iteratables = statement.content.for.iteratables;
|
||||
|
||||
switch (statement.content.for.kind)
|
||||
{
|
||||
.slice =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.range =>
|
||||
{
|
||||
>local = statement.content.for.first_local;
|
||||
assert(local != zero);
|
||||
assert(!local.next);
|
||||
assert(!local.variable.type);
|
||||
|
||||
assert(kinds.length == 1);
|
||||
|
||||
if (iteratables.length == 2)
|
||||
{
|
||||
>start = iteratables[0];
|
||||
>end = iteratables[1];
|
||||
|
||||
>local_type: &Type = zero;
|
||||
|
||||
if (value_is_constant(start))
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
analyze_binary_type(module, start, end, 0, 0, 0, 0);
|
||||
assert(start.type == end.type);
|
||||
local_type = start.type;
|
||||
}
|
||||
|
||||
assert(local_type != zero);
|
||||
|
||||
for (iteratable: iteratables)
|
||||
{
|
||||
if (!iteratable.type)
|
||||
{
|
||||
analyze_type(module, iteratable, local_type, zero);
|
||||
}
|
||||
}
|
||||
|
||||
local.variable.type = local_type;
|
||||
emit_local(module, local);
|
||||
emit_value(module, start, .memory, 0);
|
||||
|
||||
>index_alloca = local.variable.storage.llvm;
|
||||
|
||||
create_store(module, {
|
||||
.source = start.llvm,
|
||||
.destination = index_alloca,
|
||||
.type = local_type,
|
||||
zero,
|
||||
});
|
||||
|
||||
LLVMBuildBr(module.llvm.builder, entry_block);
|
||||
LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block);
|
||||
|
||||
>header_index_load = create_load(module, {
|
||||
.type = local_type,
|
||||
.pointer = index_alloca,
|
||||
zero,
|
||||
});
|
||||
|
||||
emit_value(module, end, .abi, 0);
|
||||
|
||||
>length_value = end.llvm;
|
||||
>index_compare = LLVMBuildICmp(module.llvm.builder, .ult, header_index_load, length_value, "");
|
||||
LLVMBuildCondBr(module.llvm.builder, index_compare, body_block, exit_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(module.llvm.builder, body_block);
|
||||
analyze_statement(module, &statement.content.for.scope, statement.content.for.predicate);
|
||||
|
||||
if (LLVMGetInsertBlock(module.llvm.builder))
|
||||
{
|
||||
LLVMBuildBr(module.llvm.builder, continue_block);
|
||||
}
|
||||
|
||||
LLVMPositionBuilderAtEnd(module.llvm.builder, continue_block);
|
||||
|
||||
>continue_index_load = create_load(module, {
|
||||
.type = local_type,
|
||||
.pointer = index_alloca,
|
||||
zero,
|
||||
});
|
||||
|
||||
>inc = LLVMBuildAdd(module.llvm.builder, continue_index_load, LLVMConstInt(local_type.llvm.abi, 1, 0), "");
|
||||
|
||||
create_store(module, {
|
||||
.source = inc,
|
||||
.destination = index_alloca,
|
||||
.type = local_type,
|
||||
zero,
|
||||
});
|
||||
|
||||
LLVMBuildBr(module.llvm.builder, entry_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: case for reverse range
|
||||
#trap();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// END OF SCOPE
|
||||
module.llvm.continue_block = previous_continue_block;
|
||||
module.llvm.exit_block = previous_exit_block;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
@ -14918,6 +15215,8 @@ names: [_][]u8 =
|
||||
"else_if_complicated",
|
||||
"basic_shortcircuiting_if",
|
||||
"shortcircuiting_if",
|
||||
"field_access_left_assign",
|
||||
"for_each",
|
||||
];
|
||||
|
||||
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32
|
||||
|
Loading…
x
Reference in New Issue
Block a user