For each
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (pull_request) Successful in 2m19s
CI / ci (ReleaseSmall, ubuntu-latest) (pull_request) Successful in 2m17s
CI / ci (ReleaseSafe, ubuntu-latest) (pull_request) Successful in 2m23s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 3m33s
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 2m19s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 2m16s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 2m22s
CI / ci (Debug, ubuntu-latest) (push) Successful in 3m32s

This commit is contained in:
David Gonzalez Martin 2025-04-18 19:18:18 -06:00
parent 8bef7f8bd0
commit c09715b2d0
3 changed files with 385 additions and 86 deletions

View File

@ -616,12 +616,21 @@ pub const Statement = struct {
expression: *Value, expression: *Value,
@"if": If, @"if": If,
@"while": While, @"while": While,
for_each: ForEach,
@"switch": Switch, @"switch": Switch,
block: *LexicalBlock, block: *LexicalBlock,
}, },
line: u32, line: u32,
column: u32, column: u32,
const ForEach = struct {
locals: []const *Local,
left_values: []const Value.Kind,
right_values: []const *Value,
predicate: *Statement,
scope: Scope,
};
const Assignment = struct { const Assignment = struct {
left: *Value, left: *Value,
right: *Value, right: *Value,
@ -980,6 +989,7 @@ pub const Scope = struct {
global, global,
function, function,
local, local,
for_each,
}; };
}; };
@ -1044,7 +1054,6 @@ pub const Module = struct {
lexical_blocks: lib.VirtualBuffer(LexicalBlock), lexical_blocks: lib.VirtualBuffer(LexicalBlock),
statements: lib.VirtualBuffer(Statement), statements: lib.VirtualBuffer(Statement),
current_function: ?*Global = null, current_function: ?*Global = null,
current_scope: *Scope,
name: []const u8, name: []const u8,
path: []const u8, path: []const u8,
executable: [:0]const u8, executable: [:0]const u8,
@ -1639,6 +1648,7 @@ pub const Module = struct {
@"return", @"return",
@"if", @"if",
// TODO: make `unreachable` a statement start keyword? // TODO: make `unreachable` a statement start keyword?
@"for",
@"while", @"while",
@"switch", @"switch",
}; };
@ -2234,30 +2244,30 @@ pub const Module = struct {
after: ?*const Rule.Function, after: ?*const Rule.Function,
precedence: Precedence, precedence: Precedence,
const Function = fn (noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value; const Function = fn (noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value;
}; };
fn parse_value(module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn parse_value(module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
assert(value_builder.precedence == .none); assert(value_builder.precedence == .none);
assert(value_builder.left == null); assert(value_builder.left == null);
const value = module.parse_precedence(function, value_builder.with_precedence(.assignment)); const value = module.parse_precedence(function, scope, value_builder.with_precedence(.assignment));
return value; return value;
} }
fn parse_precedence(module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn parse_precedence(module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
assert(value_builder.token == .none); assert(value_builder.token == .none);
const token = module.tokenize(); const token = module.tokenize();
const rule = &rules[@intFromEnum(token)]; const rule = &rules[@intFromEnum(token)];
if (rule.before) |before| { if (rule.before) |before| {
const left = before(module, function, value_builder.with_precedence(.none).with_token(token)); const left = before(module, function, scope, value_builder.with_precedence(.none).with_token(token));
const result = module.parse_precedence_left(function, value_builder.with_left(left)); const result = module.parse_precedence_left(function, scope, value_builder.with_left(left));
return result; return result;
} else { } else {
module.report_error(); module.report_error();
} }
} }
fn parse_precedence_left(module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn parse_precedence_left(module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
var result = value_builder.left; var result = value_builder.left;
_ = &result; _ = &result;
const precedence = value_builder.precedence; const precedence = value_builder.precedence;
@ -2279,14 +2289,14 @@ pub const Module = struct {
const after_rule = token_rule.after orelse module.report_error(); const after_rule = token_rule.after orelse module.report_error();
const old = result; const old = result;
const new = after_rule(module, function, value_builder.with_token(token).with_precedence(.none).with_left(old)); const new = after_rule(module, function, scope, value_builder.with_token(token).with_precedence(.none).with_left(old));
result = new; result = new;
} }
return result.?; return result.?;
} }
fn parse_statement(module: *Module, function: *Global, block: *LexicalBlock) *Statement { fn parse_statement(module: *Module, function: *Global, scope: *Scope) *Statement {
const statement_line = module.get_line(); const statement_line = module.get_line();
const statement_column = module.get_column(); const statement_column = module.get_column();
@ -2307,7 +2317,7 @@ pub const Module = struct {
break :b t; break :b t;
} else null; } else null;
module.expect_character('='); module.expect_character('=');
const local_value = module.parse_value(function, .{}); const local_value = module.parse_value(function, scope, .{});
const local = module.locals.add(); const local = module.locals.add();
local.* = .{ local.* = .{
.variable = .{ .variable = .{
@ -2316,18 +2326,23 @@ pub const Module = struct {
.name = local_name, .name = local_name,
.line = statement_line, .line = statement_line,
.column = statement_column, .column = statement_column,
.scope = module.current_scope, .scope = scope,
}, },
.argument_index = null, .argument_index = null,
}; };
assert(module.current_scope == &block.scope); switch (scope.kind) {
_ = block.locals.append(local); .local => {
const block: *LexicalBlock = @fieldParentPtr("scope", scope);
_ = block.locals.append(local);
},
else => @trap(),
}
break :blk .{ break :blk .{
.local = local, .local = local,
}; };
}, },
'#' => .{ '#' => .{
.expression = module.parse_value(function, .{}), .expression = module.parse_value(function, scope, .{}),
}, },
'A'...'Z', 'a'...'z' => blk: { 'A'...'Z', 'a'...'z' => blk: {
const statement_start_identifier = module.parse_identifier(); const statement_start_identifier = module.parse_identifier();
@ -2335,7 +2350,7 @@ pub const Module = struct {
if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) { if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) {
._ => @trap(), ._ => @trap(),
.@"return" => break :blk .{ .@"return" => break :blk .{
.@"return" = module.parse_value(function, .{}), .@"return" = module.parse_value(function, scope, .{}),
}, },
.@"if" => { .@"if" => {
module.skip_space(); module.skip_space();
@ -2343,14 +2358,14 @@ pub const Module = struct {
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const condition = module.parse_value(function, .{}); const condition = module.parse_value(function, scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
module.skip_space(); module.skip_space();
const if_statement = module.parse_statement(function, block); const if_statement = module.parse_statement(function, scope);
module.skip_space(); module.skip_space();
@ -2366,7 +2381,7 @@ pub const Module = struct {
} }
const else_block = switch (is_else) { const else_block = switch (is_else) {
true => module.parse_statement(function, block), true => module.parse_statement(function, scope),
false => null, false => null,
}; };
@ -2386,14 +2401,14 @@ pub const Module = struct {
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const condition = module.parse_value(function, .{}); const condition = module.parse_value(function, scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
module.skip_space(); module.skip_space();
const while_block = module.parse_block(function); const while_block = module.parse_block(function, scope);
require_semicolon = false; require_semicolon = false;
@ -2404,12 +2419,132 @@ pub const Module = struct {
}, },
}; };
}, },
.@"for" => {
module.skip_space();
module.expect_character(left_parenthesis);
module.skip_space();
statement.* = .{
.line = statement_line,
.column = statement_column,
.bb = .{
.for_each = .{
.locals = &.{},
.left_values = &.{},
.right_values = &.{},
.predicate = undefined,
.scope = .{
.line = statement_line,
.column = statement_column,
.kind = .for_each,
.parent = scope,
},
},
},
};
var local_buffer: [64]*Local = undefined;
var left_value_buffer: [64]Value.Kind = undefined;
var left_value_count: u64 = 0;
while (true) {
module.skip_space();
const is_left = switch (module.content[module.offset]) {
'&' => true,
else => false,
};
module.offset += @intFromBool(is_left);
const for_local_line = module.get_line();
const for_local_column = module.get_column();
if (is_identifier_start_ch(module.content[module.offset])) {
const identifier = module.parse_identifier();
const local = module.locals.add();
local.* = .{
.variable = .{
.type = null,
.initial_value = undefined,
.scope = &statement.bb.for_each.scope,
.name = identifier,
.line = for_local_line,
.column = for_local_column,
},
.argument_index = null,
};
local_buffer[left_value_count] = local;
left_value_buffer[left_value_count] = switch (is_left) {
true => .left,
false => .right,
};
left_value_count += 1;
} else {
@trap();
}
module.skip_space();
if (!module.consume_character_if_match(',')) {
module.expect_character(':');
break;
}
}
module.skip_space();
var right_value_buffer: [64]*Value = undefined;
var right_value_count: u64 = 0;
while (true) {
module.skip_space();
const identifier = module.parse_value(function, scope, .{
.kind = .left,
});
right_value_buffer[right_value_count] = identifier;
right_value_count += 1;
module.skip_space();
if (!module.consume_character_if_match(',')) {
module.expect_character(right_parenthesis);
break;
}
}
module.skip_space();
if (left_value_count != right_value_count) {
module.report_error();
}
const locals = module.arena.allocate(*Local, left_value_count);
@memcpy(locals, local_buffer[0..left_value_count]);
const left_values = module.arena.allocate(Value.Kind, left_value_count);
@memcpy(left_values, left_value_buffer[0..left_value_count]);
const right_values = module.arena.allocate(*Value, right_value_count);
@memcpy(right_values, right_value_buffer[0..right_value_count]);
statement.bb.for_each.locals = locals;
statement.bb.for_each.left_values = left_values;
statement.bb.for_each.right_values = right_values;
const predicate = module.parse_statement(function, &statement.bb.for_each.scope);
statement.bb.for_each.predicate = predicate;
module.skip_space();
require_semicolon = false;
break :blk statement.bb;
},
.@"switch" => { .@"switch" => {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const discriminant = module.parse_value(function, .{}); const discriminant = module.parse_value(function, scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
@ -2447,7 +2582,7 @@ pub const Module = struct {
var case_count: u64 = 0; var case_count: u64 = 0;
while (true) { while (true) {
const case_value = module.parse_value(function, .{}); const case_value = module.parse_value(function, scope, .{});
case_buffer[case_count] = case_value; case_buffer[case_count] = case_value;
case_count += 1; case_count += 1;
@ -2468,7 +2603,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
const clause_block = module.parse_block(function); const clause_block = module.parse_block(function, scope);
clause_buffer[clause_count] = .{ clause_buffer[clause_count] = .{
.values = clause_values, .values = clause_values,
@ -2500,7 +2635,7 @@ pub const Module = struct {
} else { } else {
module.offset -= statement_start_identifier.len; module.offset -= statement_start_identifier.len;
const left = module.parse_value(function, .{ const left = module.parse_value(function, scope, .{
.kind = .left, .kind = .left,
}); });
@ -2583,7 +2718,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
const right = module.parse_value(function, .{}); const right = module.parse_value(function, scope, .{});
break :blk .{ break :blk .{
.assignment = .{ .assignment = .{
@ -2598,7 +2733,7 @@ pub const Module = struct {
left_brace => blk: { left_brace => blk: {
require_semicolon = false; require_semicolon = false;
break :blk .{ break :blk .{
.block = module.parse_block(function), .block = module.parse_block(function, scope),
}; };
}, },
else => @trap(), else => @trap(),
@ -2614,8 +2749,7 @@ pub const Module = struct {
return statement; return statement;
} }
fn parse_block(module: *Module, function: *Global) *LexicalBlock { fn parse_block(module: *Module, function: *Global, parent_scope: *Scope) *LexicalBlock {
const parent_scope = module.current_scope;
const block = module.lexical_blocks.append(.{ const block = module.lexical_blocks.append(.{
.statements = .initialize(), .statements = .initialize(),
.locals = .initialize(), .locals = .initialize(),
@ -2626,8 +2760,7 @@ pub const Module = struct {
.column = module.get_column(), .column = module.get_column(),
}, },
}); });
module.current_scope = &block.scope; const scope = &block.scope;
defer module.current_scope = parent_scope;
module.expect_character(left_brace); module.expect_character(left_brace);
@ -2642,14 +2775,15 @@ pub const Module = struct {
break; break;
} }
const statement = module.parse_statement(function, block); const statement = module.parse_statement(function, scope);
_ = block.statements.append(statement); _ = block.statements.append(statement);
} }
return block; return block;
} }
fn rule_before_dot(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_dot(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
_ = scope;
_ = function; _ = function;
_ = value_builder; _ = value_builder;
module.skip_space(); module.skip_space();
@ -2664,7 +2798,8 @@ pub const Module = struct {
return value; return value;
} }
fn rule_after_dot(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_after_dot(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
_ = scope;
_ = function; _ = function;
module.skip_space(); module.skip_space();
const left = value_builder.left orelse module.report_error(); const left = value_builder.left orelse module.report_error();
@ -2683,7 +2818,8 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_string_literal(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_string_literal(noalias module: *Module, function: ?*Global, parent_scope: *Scope, value_builder: Value.Builder) *Value {
_ = parent_scope;
_ = function; _ = function;
const value = module.values.add(); const value = module.values.add();
value.* = .{ value.* = .{
@ -2694,16 +2830,17 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_identifier(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_identifier(noalias module: *Module, function: ?*Global, current_scope: *Scope, value_builder: Value.Builder) *Value {
_ = function; _ = function;
const identifier = value_builder.token.identifier; const identifier = value_builder.token.identifier;
assert(!lib.string.equal(identifier, "")); assert(!lib.string.equal(identifier, ""));
assert(!lib.string.equal(identifier, "_")); assert(!lib.string.equal(identifier, "_"));
var scope_it: ?*Scope = module.current_scope; var scope_it: ?*Scope = current_scope;
const variable = blk: while (scope_it) |scope| : (scope_it = scope.parent) { const variable = blk: while (scope_it) |scope| : (scope_it = scope.parent) {
switch (scope.kind) { switch (scope.kind) {
.global => { .global => {
assert(scope.parent == null);
const m: *Module = @fieldParentPtr("scope", scope); const m: *Module = @fieldParentPtr("scope", scope);
assert(m == module); assert(m == module);
for (module.globals.get_slice()) |*global| { for (module.globals.get_slice()) |*global| {
@ -2712,25 +2849,33 @@ pub const Module = struct {
} }
} }
assert(scope.parent == null);
}, },
.function => { .function => {
assert(scope.parent != null);
const f: *Function = @fieldParentPtr("scope", scope); const f: *Function = @fieldParentPtr("scope", scope);
for (f.arguments) |argument| { for (f.arguments) |argument| {
if (lib.string.equal(argument.variable.name, identifier)) { if (lib.string.equal(argument.variable.name, identifier)) {
break :blk &argument.variable; break :blk &argument.variable;
} }
} }
assert(scope.parent != null);
}, },
.local => { .local => {
assert(scope.parent != null);
const block: *LexicalBlock = @fieldParentPtr("scope", scope); const block: *LexicalBlock = @fieldParentPtr("scope", scope);
for (block.locals.get_slice()) |local| { for (block.locals.get_slice()) |local| {
if (lib.string.equal(local.variable.name, identifier)) { if (lib.string.equal(local.variable.name, identifier)) {
break :blk &local.variable; break :blk &local.variable;
} }
} }
},
.for_each => {
assert(scope.parent != null); assert(scope.parent != null);
const for_each: *Statement.ForEach = @fieldParentPtr("scope", scope);
for (for_each.locals) |local| {
if (lib.string.equal(local.variable.name, identifier)) {
break :blk &local.variable;
}
}
}, },
} }
} else { } else {
@ -2752,7 +2897,8 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_value_keyword(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_value_keyword(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
_ = scope;
_ = function; _ = function;
const value = module.values.add(); const value = module.values.add();
const new_value: Value = switch (value_builder.token.value_keyword) { const new_value: Value = switch (value_builder.token.value_keyword) {
@ -2770,7 +2916,7 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_value_intrinsic(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_value_intrinsic(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
const intrinsic = value_builder.token.value_intrinsic; const intrinsic = value_builder.token.value_intrinsic;
const value = module.values.add(); const value = module.values.add();
@ -2793,7 +2939,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const arg_value = module.parse_value(function, .{}); const arg_value = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
.bb = .{ .bb = .{
@ -2821,7 +2967,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const arg_value = module.parse_value(function, .{}); const arg_value = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
.bb = .{ .bb = .{
@ -2835,7 +2981,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const arg_value = module.parse_value(function, .{}); const arg_value = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
.bb = .{ .bb = .{
@ -2849,7 +2995,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const v = module.parse_value(function, .{}); const v = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
.bb = .{ .bb = .{
@ -2863,13 +3009,13 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const condition = module.parse_value(function, .{}); const condition = module.parse_value(function, scope, .{});
module.expect_character(','); module.expect_character(',');
module.skip_space(); module.skip_space();
const true_value = module.parse_value(function, .{}); const true_value = module.parse_value(function, scope, .{});
module.expect_character(','); module.expect_character(',');
module.skip_space(); module.skip_space();
const false_value = module.parse_value(function, .{}); const false_value = module.parse_value(function, scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
@ -2891,7 +3037,7 @@ pub const Module = struct {
const enum_type = module.parse_type(function); const enum_type = module.parse_type(function);
module.expect_character(','); module.expect_character(',');
module.skip_space(); module.skip_space();
const string_value = module.parse_value(function, .{}); const string_value = module.parse_value(function, scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
@ -2921,7 +3067,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const v = module.parse_value(function, .{}); const v = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
.bb = .{ .bb = .{
@ -2946,7 +3092,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const va_list = module.parse_value(function, .{}); const va_list = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
break :blk .{ break :blk .{
.bb = .{ .bb = .{
@ -2960,7 +3106,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
const va_list = module.parse_value(function, .{}); const va_list = module.parse_value(function, scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(','); module.expect_character(',');
module.skip_space(); module.skip_space();
@ -2983,7 +3129,8 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_integer(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_integer(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
_ = scope;
_ = function; _ = function;
const v = value_builder.token.integer.value; const v = value_builder.token.integer.value;
const value = module.values.add(); const value = module.values.add();
@ -2998,13 +3145,13 @@ pub const Module = struct {
return value; return value;
} }
fn rule_after_binary(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_after_binary(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
const binary_operator_token = value_builder.token; const binary_operator_token = value_builder.token;
const binary_operator_token_precedence = rules[@intFromEnum(binary_operator_token)].precedence; const binary_operator_token_precedence = rules[@intFromEnum(binary_operator_token)].precedence;
const left = value_builder.left orelse module.report_error(); const left = value_builder.left orelse module.report_error();
assert(binary_operator_token_precedence != .assignment); // TODO: this may be wrong. Assignment operator is not allowed in expressions assert(binary_operator_token_precedence != .assignment); // TODO: this may be wrong. Assignment operator is not allowed in expressions
const right_precedence = if (binary_operator_token_precedence == .assignment) .assignment else binary_operator_token_precedence.increment(); const right_precedence = if (binary_operator_token_precedence == .assignment) .assignment else binary_operator_token_precedence.increment();
const right = module.parse_precedence(function, value_builder.with_precedence(right_precedence).with_token(.none).with_left(null)); const right = module.parse_precedence(function, scope, value_builder.with_precedence(right_precedence).with_token(.none).with_left(null));
const binary_operation_kind: Binary.Id = switch (binary_operator_token) { const binary_operation_kind: Binary.Id = switch (binary_operator_token) {
.none => unreachable, .none => unreachable,
@ -3045,7 +3192,7 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_unary(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_unary(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
assert(value_builder.left == null); assert(value_builder.left == null);
const unary_token = value_builder.token; const unary_token = value_builder.token;
const unary_id: Unary.Id = switch (unary_token) { const unary_id: Unary.Id = switch (unary_token) {
@ -3058,7 +3205,7 @@ pub const Module = struct {
else => @trap(), else => @trap(),
}; };
const right = module.parse_precedence(function, value_builder.with_precedence(.prefix).with_token(.none).with_kind(if (unary_id == .@"&") .left else value_builder.kind)); const right = module.parse_precedence(function, scope, value_builder.with_precedence(.prefix).with_token(.none).with_kind(if (unary_id == .@"&") .left else value_builder.kind));
const value = module.values.add(); const value = module.values.add();
value.* = .{ value.* = .{
@ -3072,7 +3219,8 @@ pub const Module = struct {
return value; return value;
} }
fn rule_after_dereference(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_after_dereference(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
_ = scope;
_ = function; _ = function;
const value = module.values.add(); const value = module.values.add();
value.* = .{ value.* = .{
@ -3083,7 +3231,7 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_brace(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_brace(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
assert(value_builder.left == null); assert(value_builder.left == null);
var name_buffer: [64][]const u8 = undefined; var name_buffer: [64][]const u8 = undefined;
@ -3108,7 +3256,7 @@ pub const Module = struct {
module.skip_space(); module.skip_space();
const value = module.parse_value(function, .{}); const value = module.parse_value(function, scope, .{});
value_buffer[field_count] = value; value_buffer[field_count] = value;
module.skip_space(); module.skip_space();
@ -3162,7 +3310,7 @@ pub const Module = struct {
} }
// Array initialization // Array initialization
fn rule_before_bracket(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_bracket(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
assert(value_builder.left == null); assert(value_builder.left == null);
var value_buffer: [64]*Value = undefined; var value_buffer: [64]*Value = undefined;
@ -3175,7 +3323,7 @@ pub const Module = struct {
if (module.consume_character_if_match(right_bracket)) { if (module.consume_character_if_match(right_bracket)) {
break; break;
} }
const v = module.parse_value(function, .{}); const v = module.parse_value(function, scope, .{});
value_buffer[element_count] = v; value_buffer[element_count] = v;
_ = module.consume_character_if_match(','); _ = module.consume_character_if_match(',');
@ -3198,9 +3346,9 @@ pub const Module = struct {
} }
// Array-like subscript // Array-like subscript
fn rule_after_bracket(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_after_bracket(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
const left = value_builder.left orelse module.report_error(); const left = value_builder.left orelse module.report_error();
const index = module.parse_value(function, .{}); const index = module.parse_value(function, scope, .{});
const value = module.values.add(); const value = module.values.add();
if (module.consume_character_if_match(right_bracket)) { if (module.consume_character_if_match(right_bracket)) {
value.* = .{ value.* = .{
@ -3218,7 +3366,7 @@ pub const Module = struct {
const end = switch (module.consume_character_if_match(right_bracket)) { const end = switch (module.consume_character_if_match(right_bracket)) {
true => null, true => null,
false => b: { false => b: {
const end = module.parse_value(function, .{}); const end = module.parse_value(function, scope, .{});
module.expect_character(right_bracket); module.expect_character(right_bracket);
break :b end; break :b end;
}, },
@ -3236,15 +3384,15 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_parenthesis(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_before_parenthesis(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
_ = value_builder; _ = value_builder;
module.skip_space(); module.skip_space();
const v = module.parse_value(function, .{}); const v = module.parse_value(function, scope, .{});
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
return v; return v;
} }
fn rule_after_call(noalias module: *Module, function: ?*Global, value_builder: Value.Builder) *Value { fn rule_after_call(noalias module: *Module, function: ?*Global, scope: *Scope, value_builder: Value.Builder) *Value {
const may_be_callable = value_builder.left orelse module.report_error(); const may_be_callable = value_builder.left orelse module.report_error();
assert(value_builder.token == .@"("); assert(value_builder.token == .@"(");
var semantic_argument_count: u32 = 0; var semantic_argument_count: u32 = 0;
@ -3259,7 +3407,7 @@ pub const Module = struct {
break; break;
} }
const argument = module.parse_value(function, .{}); const argument = module.parse_value(function, scope, .{});
const argument_index = semantic_argument_count; const argument_index = semantic_argument_count;
semantic_argument_buffer[argument_index] = argument; semantic_argument_buffer[argument_index] = argument;
@ -3570,11 +3718,7 @@ pub const Module = struct {
} }
} }
const global_scope = module.current_scope; storage.bb.function.main_block = module.parse_block(global, &storage.bb.function.scope);
module.current_scope = &storage.bb.function.scope;
defer module.current_scope = global_scope;
storage.bb.function.main_block = module.parse_block(global);
} else { } else {
storage.bb = .external_function; storage.bb = .external_function;
} }
@ -3834,7 +3978,7 @@ pub const Module = struct {
} }
if (!global_keyword) { if (!global_keyword) {
const v = module.parse_value(null, .{}); const v = module.parse_value(null, &module.scope, .{});
module.skip_space(); module.skip_space();
module.expect_character(';'); module.expect_character(';');
@ -5159,7 +5303,7 @@ pub const Module = struct {
module.emit_value(function, value, type_kind); module.emit_value(function, value, type_kind);
} }
pub fn analyze_field_access_type(module: *Module, function: ?*Global, value: *Value) *Type { pub fn analyze_field_access_type(module: *Module, function: ?*Global, value: *Value, expected_type: ?*Type) *Type {
switch (value.bb) { switch (value.bb) {
.field_access => |field_access| { .field_access => |field_access| {
module.analyze_value_type(function, field_access.aggregate, .{}); module.analyze_value_type(function, field_access.aggregate, .{});
@ -5204,6 +5348,17 @@ pub const Module = struct {
.right => field_type, .right => field_type,
}; };
}, },
.array => {
if (expected_type) |t| {
if (t.bb != .integer) {
module.report_error();
}
// TODO: see if the count fits into the integer type
return t;
} else {
@trap();
}
},
.pointer => module.report_error(), .pointer => module.report_error(),
else => @trap(), else => @trap(),
} }
@ -5549,7 +5704,7 @@ pub const Module = struct {
else => @trap(), else => @trap(),
}, },
.field_access => { .field_access => {
const field_type = module.analyze_field_access_type(function, value); const field_type = module.analyze_field_access_type(function, value, expected_type);
if (field_type != expected_type) { if (field_type != expected_type) {
module.report_error(); module.report_error();
} }
@ -6001,7 +6156,7 @@ pub const Module = struct {
}, },
} }
}, },
.field_access => module.analyze_field_access_type(function, value), .field_access => module.analyze_field_access_type(function, value, null),
.string_literal => module.get_slice_type(.{ .type = module.integer_type(8, false) }), .string_literal => module.get_slice_type(.{ .type = module.integer_type(8, false) }),
.slice_expression => |slice_expression| blk: { .slice_expression => |slice_expression| blk: {
module.analyze_value_type(function, slice_expression.array_like, .{}); module.analyze_value_type(function, slice_expression.array_like, .{});
@ -6608,6 +6763,7 @@ pub const Module = struct {
const trunc = module.llvm.builder.create_truncate(shift, field.type.llvm.abi.?); const trunc = module.llvm.builder.create_truncate(shift, field.type.llvm.abi.?);
break :blk trunc; break :blk trunc;
}, },
.array => |array| break :blk value_type.get_llvm(type_kind).to_integer().get_constant(array.element_count, 0).to_value(),
else => @trap(), else => @trap(),
} }
}, },
@ -6739,13 +6895,13 @@ pub const Module = struct {
value.llvm = llvm_value; value.llvm = llvm_value;
} }
pub fn analyze_statement(module: *Module, function: *Global, block: *LexicalBlock, statement: *Statement, last_line: *u32, last_column: *u32, last_statement_debug_location: **llvm.DI.Location) void { pub fn analyze_statement(module: *Module, function: *Global, scope: *Scope, statement: *Statement, last_line: *u32, last_column: *u32, last_statement_debug_location: **llvm.DI.Location) void {
const llvm_function = function.variable.storage.?.llvm.?.to_function(); const llvm_function = function.variable.storage.?.llvm.?.to_function();
const current_function = &function.variable.storage.?.bb.function; const current_function = &function.variable.storage.?.bb.function;
if (module.has_debug_info) { if (module.has_debug_info) {
if (statement.line != last_line.* or statement.column != last_column.*) { if (statement.line != last_line.* or statement.column != last_column.*) {
const inlined_at: ?*llvm.DI.Metadata = null; // TODO const inlined_at: ?*llvm.DI.Metadata = null; // TODO
last_statement_debug_location.* = llvm.DI.create_debug_location(module.llvm.context, statement.line, statement.column, block.scope.llvm.?, inlined_at); last_statement_debug_location.* = llvm.DI.create_debug_location(module.llvm.context, statement.line, statement.column, scope.llvm.?, inlined_at);
module.llvm.builder.set_current_debug_location(last_statement_debug_location.*); module.llvm.builder.set_current_debug_location(last_statement_debug_location.*);
last_line.* = statement.line; last_line.* = statement.line;
last_column.* = statement.column; last_column.* = statement.column;
@ -6980,14 +7136,14 @@ pub const Module = struct {
current_function.exit_block = exit_block; current_function.exit_block = exit_block;
module.analyze_statement(function, block, if_statement.if_statement, last_line, last_line, last_statement_debug_location); module.analyze_statement(function, scope, if_statement.if_statement, last_line, last_line, last_statement_debug_location);
if (module.llvm.builder.get_insert_block() != null) { if (module.llvm.builder.get_insert_block() != null) {
_ = module.llvm.builder.create_branch(exit_block); _ = module.llvm.builder.create_branch(exit_block);
} }
module.llvm.builder.position_at_end(not_taken_block); module.llvm.builder.position_at_end(not_taken_block);
if (if_statement.else_statement) |else_statement| { if (if_statement.else_statement) |else_statement| {
module.analyze_statement(function, block, else_statement, last_line, last_line, last_statement_debug_location); module.analyze_statement(function, scope, else_statement, last_line, last_line, last_statement_debug_location);
} }
if (module.llvm.builder.get_insert_block() != null) { if (module.llvm.builder.get_insert_block() != null) {
@ -7102,6 +7258,119 @@ pub const Module = struct {
} }
}, },
.block => |child_block| module.analyze_block(function, child_block), .block => |child_block| module.analyze_block(function, child_block),
.for_each => |*for_loop| {
if (module.has_debug_info) {
const lexical_block = module.llvm.di_builder.create_lexical_block(for_loop.scope.parent.?.llvm.?, module.llvm.file, for_loop.scope.line, for_loop.scope.column);
for_loop.scope.llvm = lexical_block.to_scope();
}
for (for_loop.locals, for_loop.left_values, for_loop.right_values) |local, kind, right| {
assert(right.kind == .left);
module.analyze_value_type(function, right, .{});
const pointer_type = right.type.?;
if (pointer_type.bb != .pointer) {
module.report_error();
}
const aggregate_type = pointer_type.bb.pointer.type;
const child_type = switch (aggregate_type.bb) {
.array => |array| array.element_type,
else => @trap(),
};
assert(local.variable.type == null);
const local_type = switch (kind) {
.left => module.get_pointer_type(.{ .type = child_type }),
.right => child_type,
};
local.variable.type = local_type;
module.emit_local_storage(local, last_statement_debug_location.*);
module.emit_value(function, right, .memory);
}
const length = for (for_loop.right_values) |right| {
const pointer_type = right.type.?;
if (pointer_type.bb != .pointer) {
module.report_error();
}
const aggregate_type = pointer_type.bb.pointer.type;
const length = switch (aggregate_type.bb) {
.array => |array| array.element_count,
else => @trap(),
};
break length;
} else unreachable;
const index_type = module.integer_type(64, false);
index_type.resolve(module);
const index_zero = index_type.llvm.abi.?.get_zero().to_value();
const index_alloca = module.create_alloca(.{ .type = index_type, .name = "foreach.index" });
_ = module.create_store(.{ .type = index_type, .source_value = index_zero, .destination_value = index_alloca });
const loop_entry_block = module.llvm.context.create_basic_block("foreach.entry", llvm_function);
const loop_body_block = module.llvm.context.create_basic_block("foreach.body", llvm_function);
const loop_continue_block = module.llvm.context.create_basic_block("foreach.continue", llvm_function);
const loop_exit_block = module.llvm.context.create_basic_block("foreach.exit", llvm_function);
_ = module.llvm.builder.create_branch(loop_entry_block);
module.llvm.builder.position_at_end(loop_entry_block);
const header_index_load = module.create_load(.{ .type = index_type, .value = index_alloca });
const index_compare = module.llvm.builder.create_integer_compare(.ult, header_index_load, index_type.llvm.abi.?.to_integer().get_constant(length, 0).to_value());
_ = module.llvm.builder.create_conditional_branch(index_compare, loop_body_block, loop_exit_block);
module.llvm.builder.position_at_end(loop_body_block);
const body_index_load = module.create_load(.{ .type = index_type, .value = index_alloca });
for (for_loop.locals, for_loop.left_values, for_loop.right_values) |local, kind, right| {
const element_pointer_value = switch (right.type.?.bb.pointer.type.bb) {
.array => module.llvm.builder.create_gep(.{
.type = right.type.?.bb.pointer.type.llvm.memory.?,
.aggregate = right.llvm.?,
.indices = &.{index_zero, body_index_load},
}),
else => @trap(),
};
switch (kind) {
.left => {
_ = module.create_store(.{
.type = local.variable.type.?,
.source_value = element_pointer_value,
.destination_value = local.variable.storage.?.llvm.?,
});
},
.right => switch (local.variable.type.?.get_evaluation_kind()) {
.scalar => {
const load = module.create_load(.{
.type = local.variable.type.?,
.value = element_pointer_value,
});
_ = module.create_store(.{
.type = local.variable.type.?,
.source_value = load,
.destination_value = local.variable.storage.?.llvm.?,
});
},
.aggregate => {
@trap();
},
.complex => @trap(),
},
}
}
module.analyze_statement(function, &for_loop.scope, for_loop.predicate, last_line, last_column, last_statement_debug_location);
if (module.llvm.builder.get_insert_block() != null) {
_ = module.llvm.builder.create_branch(loop_continue_block);
}
module.llvm.builder.position_at_end(loop_continue_block);
const continue_index_load = module.create_load(.{ .type = index_type, .value = index_alloca });
const add = module.llvm.builder.create_add(continue_index_load, index_type.llvm.abi.?.to_integer().get_constant(1, 0).to_value());
_ = module.create_store(.{ .type = index_type, .source_value = add, .destination_value = index_alloca });
_ = module.llvm.builder.create_branch(loop_entry_block);
module.llvm.builder.position_at_end(loop_exit_block);
},
} }
} }
@ -7116,7 +7385,7 @@ pub const Module = struct {
var last_statement_debug_location: *llvm.DI.Location = undefined; var last_statement_debug_location: *llvm.DI.Location = undefined;
for (block.statements.get_slice()) |statement| { for (block.statements.get_slice()) |statement| {
module.analyze_statement(function, block, statement, &last_line, &last_column, &last_statement_debug_location); module.analyze_statement(function, &block.scope, statement, &last_line, &last_column, &last_statement_debug_location);
} }
} }
@ -8789,7 +9058,6 @@ pub fn compile(arena: *Arena, options: Options) void {
.void_type = void_type, .void_type = void_type,
.noreturn_type = noreturn_type, .noreturn_type = noreturn_type,
.void_value = void_value, .void_value = void_value,
.current_scope = undefined,
.scope = .{ .scope = .{
.kind = .global, .kind = .global,
.column = 0, .column = 0,
@ -8805,8 +9073,6 @@ pub fn compile(arena: *Arena, options: Options) void {
.silent = options.silent, .silent = options.silent,
}; };
module.current_scope = &module.scope;
module.parse(); module.parse();
module.emit(); module.emit();
} }

View File

@ -316,4 +316,5 @@ const names = &[_][]const u8{
"else_if_complicated", "else_if_complicated",
"shortcircuiting_if", "shortcircuiting_if",
"field_access_left_assign", "field_access_left_assign",
"for_each",
}; };

32
tests/for_each.bbb Normal file
View File

@ -0,0 +1,32 @@
require = fn (ok: u1) void
{
if (!ok) #trap();
}
[export] main = fn [cc(c)] () s32
{
>array: [_]u32 = [5, 3, 2];
>counter: u32 = 0;
for (e : array)
{
counter += e;
}
require(counter == 10);
for (&e : array)
{
e.& += 1;
}
>new_counter: u32 = 0;
for (e : array)
{
new_counter += e;
}
require(new_counter == counter + array.length);
return 0;
}