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
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:
parent
8bef7f8bd0
commit
c09715b2d0
@ -616,12 +616,21 @@ pub const Statement = struct {
|
||||
expression: *Value,
|
||||
@"if": If,
|
||||
@"while": While,
|
||||
for_each: ForEach,
|
||||
@"switch": Switch,
|
||||
block: *LexicalBlock,
|
||||
},
|
||||
line: 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 {
|
||||
left: *Value,
|
||||
right: *Value,
|
||||
@ -980,6 +989,7 @@ pub const Scope = struct {
|
||||
global,
|
||||
function,
|
||||
local,
|
||||
for_each,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1044,7 +1054,6 @@ pub const Module = struct {
|
||||
lexical_blocks: lib.VirtualBuffer(LexicalBlock),
|
||||
statements: lib.VirtualBuffer(Statement),
|
||||
current_function: ?*Global = null,
|
||||
current_scope: *Scope,
|
||||
name: []const u8,
|
||||
path: []const u8,
|
||||
executable: [:0]const u8,
|
||||
@ -1639,6 +1648,7 @@ pub const Module = struct {
|
||||
@"return",
|
||||
@"if",
|
||||
// TODO: make `unreachable` a statement start keyword?
|
||||
@"for",
|
||||
@"while",
|
||||
@"switch",
|
||||
};
|
||||
@ -2234,30 +2244,30 @@ pub const Module = struct {
|
||||
after: ?*const Rule.Function,
|
||||
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.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;
|
||||
}
|
||||
|
||||
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);
|
||||
const token = module.tokenize();
|
||||
const rule = &rules[@intFromEnum(token)];
|
||||
if (rule.before) |before| {
|
||||
const left = before(module, function, value_builder.with_precedence(.none).with_token(token));
|
||||
const result = module.parse_precedence_left(function, value_builder.with_left(left));
|
||||
const left = before(module, function, scope, value_builder.with_precedence(.none).with_token(token));
|
||||
const result = module.parse_precedence_left(function, scope, value_builder.with_left(left));
|
||||
return result;
|
||||
} else {
|
||||
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;
|
||||
_ = &result;
|
||||
const precedence = value_builder.precedence;
|
||||
@ -2279,14 +2289,14 @@ pub const Module = struct {
|
||||
|
||||
const after_rule = token_rule.after orelse module.report_error();
|
||||
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;
|
||||
}
|
||||
|
||||
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_column = module.get_column();
|
||||
|
||||
@ -2307,7 +2317,7 @@ pub const Module = struct {
|
||||
break :b t;
|
||||
} else null;
|
||||
module.expect_character('=');
|
||||
const local_value = module.parse_value(function, .{});
|
||||
const local_value = module.parse_value(function, scope, .{});
|
||||
const local = module.locals.add();
|
||||
local.* = .{
|
||||
.variable = .{
|
||||
@ -2316,18 +2326,23 @@ pub const Module = struct {
|
||||
.name = local_name,
|
||||
.line = statement_line,
|
||||
.column = statement_column,
|
||||
.scope = module.current_scope,
|
||||
.scope = scope,
|
||||
},
|
||||
.argument_index = null,
|
||||
};
|
||||
assert(module.current_scope == &block.scope);
|
||||
_ = block.locals.append(local);
|
||||
switch (scope.kind) {
|
||||
.local => {
|
||||
const block: *LexicalBlock = @fieldParentPtr("scope", scope);
|
||||
_ = block.locals.append(local);
|
||||
},
|
||||
else => @trap(),
|
||||
}
|
||||
break :blk .{
|
||||
.local = local,
|
||||
};
|
||||
},
|
||||
'#' => .{
|
||||
.expression = module.parse_value(function, .{}),
|
||||
.expression = module.parse_value(function, scope, .{}),
|
||||
},
|
||||
'A'...'Z', 'a'...'z' => blk: {
|
||||
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) {
|
||||
._ => @trap(),
|
||||
.@"return" => break :blk .{
|
||||
.@"return" = module.parse_value(function, .{}),
|
||||
.@"return" = module.parse_value(function, scope, .{}),
|
||||
},
|
||||
.@"if" => {
|
||||
module.skip_space();
|
||||
@ -2343,14 +2358,14 @@ pub const Module = struct {
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
|
||||
const condition = module.parse_value(function, .{});
|
||||
const condition = module.parse_value(function, scope, .{});
|
||||
|
||||
module.skip_space();
|
||||
module.expect_character(right_parenthesis);
|
||||
|
||||
module.skip_space();
|
||||
|
||||
const if_statement = module.parse_statement(function, block);
|
||||
const if_statement = module.parse_statement(function, scope);
|
||||
|
||||
module.skip_space();
|
||||
|
||||
@ -2366,7 +2381,7 @@ pub const Module = struct {
|
||||
}
|
||||
|
||||
const else_block = switch (is_else) {
|
||||
true => module.parse_statement(function, block),
|
||||
true => module.parse_statement(function, scope),
|
||||
false => null,
|
||||
};
|
||||
|
||||
@ -2386,14 +2401,14 @@ pub const Module = struct {
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
|
||||
const condition = module.parse_value(function, .{});
|
||||
const condition = module.parse_value(function, scope, .{});
|
||||
|
||||
module.skip_space();
|
||||
module.expect_character(right_parenthesis);
|
||||
|
||||
module.skip_space();
|
||||
|
||||
const while_block = module.parse_block(function);
|
||||
const while_block = module.parse_block(function, scope);
|
||||
|
||||
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" => {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
|
||||
const discriminant = module.parse_value(function, .{});
|
||||
const discriminant = module.parse_value(function, scope, .{});
|
||||
|
||||
module.skip_space();
|
||||
module.expect_character(right_parenthesis);
|
||||
@ -2447,7 +2582,7 @@ pub const Module = struct {
|
||||
var case_count: u64 = 0;
|
||||
|
||||
while (true) {
|
||||
const case_value = module.parse_value(function, .{});
|
||||
const case_value = module.parse_value(function, scope, .{});
|
||||
case_buffer[case_count] = case_value;
|
||||
case_count += 1;
|
||||
|
||||
@ -2468,7 +2603,7 @@ pub const Module = struct {
|
||||
|
||||
module.skip_space();
|
||||
|
||||
const clause_block = module.parse_block(function);
|
||||
const clause_block = module.parse_block(function, scope);
|
||||
|
||||
clause_buffer[clause_count] = .{
|
||||
.values = clause_values,
|
||||
@ -2500,7 +2635,7 @@ pub const Module = struct {
|
||||
} else {
|
||||
module.offset -= statement_start_identifier.len;
|
||||
|
||||
const left = module.parse_value(function, .{
|
||||
const left = module.parse_value(function, scope, .{
|
||||
.kind = .left,
|
||||
});
|
||||
|
||||
@ -2583,7 +2718,7 @@ pub const Module = struct {
|
||||
|
||||
module.skip_space();
|
||||
|
||||
const right = module.parse_value(function, .{});
|
||||
const right = module.parse_value(function, scope, .{});
|
||||
|
||||
break :blk .{
|
||||
.assignment = .{
|
||||
@ -2598,7 +2733,7 @@ pub const Module = struct {
|
||||
left_brace => blk: {
|
||||
require_semicolon = false;
|
||||
break :blk .{
|
||||
.block = module.parse_block(function),
|
||||
.block = module.parse_block(function, scope),
|
||||
};
|
||||
},
|
||||
else => @trap(),
|
||||
@ -2614,8 +2749,7 @@ pub const Module = struct {
|
||||
return statement;
|
||||
}
|
||||
|
||||
fn parse_block(module: *Module, function: *Global) *LexicalBlock {
|
||||
const parent_scope = module.current_scope;
|
||||
fn parse_block(module: *Module, function: *Global, parent_scope: *Scope) *LexicalBlock {
|
||||
const block = module.lexical_blocks.append(.{
|
||||
.statements = .initialize(),
|
||||
.locals = .initialize(),
|
||||
@ -2626,8 +2760,7 @@ pub const Module = struct {
|
||||
.column = module.get_column(),
|
||||
},
|
||||
});
|
||||
module.current_scope = &block.scope;
|
||||
defer module.current_scope = parent_scope;
|
||||
const scope = &block.scope;
|
||||
|
||||
module.expect_character(left_brace);
|
||||
|
||||
@ -2642,14 +2775,15 @@ pub const Module = struct {
|
||||
break;
|
||||
}
|
||||
|
||||
const statement = module.parse_statement(function, block);
|
||||
const statement = module.parse_statement(function, scope);
|
||||
_ = block.statements.append(statement);
|
||||
}
|
||||
|
||||
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;
|
||||
_ = value_builder;
|
||||
module.skip_space();
|
||||
@ -2664,7 +2798,8 @@ pub const Module = struct {
|
||||
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;
|
||||
module.skip_space();
|
||||
const left = value_builder.left orelse module.report_error();
|
||||
@ -2683,7 +2818,8 @@ pub const Module = struct {
|
||||
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;
|
||||
const value = module.values.add();
|
||||
value.* = .{
|
||||
@ -2694,16 +2830,17 @@ pub const Module = struct {
|
||||
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;
|
||||
const identifier = value_builder.token.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) {
|
||||
switch (scope.kind) {
|
||||
.global => {
|
||||
assert(scope.parent == null);
|
||||
const m: *Module = @fieldParentPtr("scope", scope);
|
||||
assert(m == module);
|
||||
for (module.globals.get_slice()) |*global| {
|
||||
@ -2712,25 +2849,33 @@ pub const Module = struct {
|
||||
}
|
||||
}
|
||||
|
||||
assert(scope.parent == null);
|
||||
},
|
||||
.function => {
|
||||
assert(scope.parent != null);
|
||||
const f: *Function = @fieldParentPtr("scope", scope);
|
||||
for (f.arguments) |argument| {
|
||||
if (lib.string.equal(argument.variable.name, identifier)) {
|
||||
break :blk &argument.variable;
|
||||
}
|
||||
}
|
||||
assert(scope.parent != null);
|
||||
},
|
||||
.local => {
|
||||
assert(scope.parent != null);
|
||||
const block: *LexicalBlock = @fieldParentPtr("scope", scope);
|
||||
for (block.locals.get_slice()) |local| {
|
||||
if (lib.string.equal(local.variable.name, identifier)) {
|
||||
break :blk &local.variable;
|
||||
}
|
||||
}
|
||||
},
|
||||
.for_each => {
|
||||
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 {
|
||||
@ -2752,7 +2897,8 @@ pub const Module = struct {
|
||||
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;
|
||||
const value = module.values.add();
|
||||
const new_value: Value = switch (value_builder.token.value_keyword) {
|
||||
@ -2770,7 +2916,7 @@ pub const Module = struct {
|
||||
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 value = module.values.add();
|
||||
|
||||
@ -2793,7 +2939,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const arg_value = module.parse_value(function, .{});
|
||||
const arg_value = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
.bb = .{
|
||||
@ -2821,7 +2967,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const arg_value = module.parse_value(function, .{});
|
||||
const arg_value = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
.bb = .{
|
||||
@ -2835,7 +2981,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const arg_value = module.parse_value(function, .{});
|
||||
const arg_value = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
.bb = .{
|
||||
@ -2849,7 +2995,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const v = module.parse_value(function, .{});
|
||||
const v = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
.bb = .{
|
||||
@ -2863,13 +3009,13 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const condition = module.parse_value(function, .{});
|
||||
const condition = module.parse_value(function, scope, .{});
|
||||
module.expect_character(',');
|
||||
module.skip_space();
|
||||
const true_value = module.parse_value(function, .{});
|
||||
const true_value = module.parse_value(function, scope, .{});
|
||||
module.expect_character(',');
|
||||
module.skip_space();
|
||||
const false_value = module.parse_value(function, .{});
|
||||
const false_value = module.parse_value(function, scope, .{});
|
||||
module.skip_space();
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
@ -2891,7 +3037,7 @@ pub const Module = struct {
|
||||
const enum_type = module.parse_type(function);
|
||||
module.expect_character(',');
|
||||
module.skip_space();
|
||||
const string_value = module.parse_value(function, .{});
|
||||
const string_value = module.parse_value(function, scope, .{});
|
||||
module.skip_space();
|
||||
module.expect_character(right_parenthesis);
|
||||
|
||||
@ -2921,7 +3067,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const v = module.parse_value(function, .{});
|
||||
const v = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
.bb = .{
|
||||
@ -2946,7 +3092,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const va_list = module.parse_value(function, .{});
|
||||
const va_list = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
break :blk .{
|
||||
.bb = .{
|
||||
@ -2960,7 +3106,7 @@ pub const Module = struct {
|
||||
module.skip_space();
|
||||
module.expect_character(left_parenthesis);
|
||||
module.skip_space();
|
||||
const va_list = module.parse_value(function, .{});
|
||||
const va_list = module.parse_value(function, scope, .{});
|
||||
module.skip_space();
|
||||
module.expect_character(',');
|
||||
module.skip_space();
|
||||
@ -2983,7 +3129,8 @@ pub const Module = struct {
|
||||
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;
|
||||
const v = value_builder.token.integer.value;
|
||||
const value = module.values.add();
|
||||
@ -2998,13 +3145,13 @@ pub const Module = struct {
|
||||
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_precedence = rules[@intFromEnum(binary_operator_token)].precedence;
|
||||
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
|
||||
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) {
|
||||
.none => unreachable,
|
||||
@ -3045,7 +3192,7 @@ pub const Module = struct {
|
||||
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);
|
||||
const unary_token = value_builder.token;
|
||||
const unary_id: Unary.Id = switch (unary_token) {
|
||||
@ -3058,7 +3205,7 @@ pub const Module = struct {
|
||||
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();
|
||||
value.* = .{
|
||||
@ -3072,7 +3219,8 @@ pub const Module = struct {
|
||||
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;
|
||||
const value = module.values.add();
|
||||
value.* = .{
|
||||
@ -3083,7 +3231,7 @@ pub const Module = struct {
|
||||
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);
|
||||
|
||||
var name_buffer: [64][]const u8 = undefined;
|
||||
@ -3108,7 +3256,7 @@ pub const Module = struct {
|
||||
|
||||
module.skip_space();
|
||||
|
||||
const value = module.parse_value(function, .{});
|
||||
const value = module.parse_value(function, scope, .{});
|
||||
value_buffer[field_count] = value;
|
||||
module.skip_space();
|
||||
|
||||
@ -3162,7 +3310,7 @@ pub const Module = struct {
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
var value_buffer: [64]*Value = undefined;
|
||||
@ -3175,7 +3323,7 @@ pub const Module = struct {
|
||||
if (module.consume_character_if_match(right_bracket)) {
|
||||
break;
|
||||
}
|
||||
const v = module.parse_value(function, .{});
|
||||
const v = module.parse_value(function, scope, .{});
|
||||
value_buffer[element_count] = v;
|
||||
|
||||
_ = module.consume_character_if_match(',');
|
||||
@ -3198,9 +3346,9 @@ pub const Module = struct {
|
||||
}
|
||||
|
||||
// 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 index = module.parse_value(function, .{});
|
||||
const index = module.parse_value(function, scope, .{});
|
||||
const value = module.values.add();
|
||||
if (module.consume_character_if_match(right_bracket)) {
|
||||
value.* = .{
|
||||
@ -3218,7 +3366,7 @@ pub const Module = struct {
|
||||
const end = switch (module.consume_character_if_match(right_bracket)) {
|
||||
true => null,
|
||||
false => b: {
|
||||
const end = module.parse_value(function, .{});
|
||||
const end = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_bracket);
|
||||
break :b end;
|
||||
},
|
||||
@ -3236,15 +3384,15 @@ pub const Module = struct {
|
||||
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;
|
||||
module.skip_space();
|
||||
const v = module.parse_value(function, .{});
|
||||
const v = module.parse_value(function, scope, .{});
|
||||
module.expect_character(right_parenthesis);
|
||||
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();
|
||||
assert(value_builder.token == .@"(");
|
||||
var semantic_argument_count: u32 = 0;
|
||||
@ -3259,7 +3407,7 @@ pub const Module = struct {
|
||||
break;
|
||||
}
|
||||
|
||||
const argument = module.parse_value(function, .{});
|
||||
const argument = module.parse_value(function, scope, .{});
|
||||
const argument_index = semantic_argument_count;
|
||||
semantic_argument_buffer[argument_index] = argument;
|
||||
|
||||
@ -3570,11 +3718,7 @@ pub const Module = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const global_scope = module.current_scope;
|
||||
module.current_scope = &storage.bb.function.scope;
|
||||
defer module.current_scope = global_scope;
|
||||
|
||||
storage.bb.function.main_block = module.parse_block(global);
|
||||
storage.bb.function.main_block = module.parse_block(global, &storage.bb.function.scope);
|
||||
} else {
|
||||
storage.bb = .external_function;
|
||||
}
|
||||
@ -3834,7 +3978,7 @@ pub const Module = struct {
|
||||
}
|
||||
|
||||
if (!global_keyword) {
|
||||
const v = module.parse_value(null, .{});
|
||||
const v = module.parse_value(null, &module.scope, .{});
|
||||
module.skip_space();
|
||||
module.expect_character(';');
|
||||
|
||||
@ -5159,7 +5303,7 @@ pub const Module = struct {
|
||||
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) {
|
||||
.field_access => |field_access| {
|
||||
module.analyze_value_type(function, field_access.aggregate, .{});
|
||||
@ -5204,6 +5348,17 @@ pub const Module = struct {
|
||||
.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(),
|
||||
else => @trap(),
|
||||
}
|
||||
@ -5549,7 +5704,7 @@ pub const Module = struct {
|
||||
else => @trap(),
|
||||
},
|
||||
.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) {
|
||||
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) }),
|
||||
.slice_expression => |slice_expression| blk: {
|
||||
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.?);
|
||||
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(),
|
||||
}
|
||||
},
|
||||
@ -6739,13 +6895,13 @@ pub const Module = struct {
|
||||
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 current_function = &function.variable.storage.?.bb.function;
|
||||
if (module.has_debug_info) {
|
||||
if (statement.line != last_line.* or statement.column != last_column.*) {
|
||||
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.*);
|
||||
last_line.* = statement.line;
|
||||
last_column.* = statement.column;
|
||||
@ -6980,14 +7136,14 @@ pub const Module = struct {
|
||||
|
||||
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) {
|
||||
_ = module.llvm.builder.create_branch(exit_block);
|
||||
}
|
||||
|
||||
module.llvm.builder.position_at_end(not_taken_block);
|
||||
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) {
|
||||
@ -7102,6 +7258,119 @@ pub const Module = struct {
|
||||
}
|
||||
},
|
||||
.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;
|
||||
|
||||
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,
|
||||
.noreturn_type = noreturn_type,
|
||||
.void_value = void_value,
|
||||
.current_scope = undefined,
|
||||
.scope = .{
|
||||
.kind = .global,
|
||||
.column = 0,
|
||||
@ -8805,8 +9073,6 @@ pub fn compile(arena: *Arena, options: Options) void {
|
||||
.silent = options.silent,
|
||||
};
|
||||
|
||||
module.current_scope = &module.scope;
|
||||
|
||||
module.parse();
|
||||
module.emit();
|
||||
}
|
||||
|
@ -316,4 +316,5 @@ const names = &[_][]const u8{
|
||||
"else_if_complicated",
|
||||
"shortcircuiting_if",
|
||||
"field_access_left_assign",
|
||||
"for_each",
|
||||
};
|
||||
|
32
tests/for_each.bbb
Normal file
32
tests/for_each.bbb
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user