commit before transpiling

This commit is contained in:
David Gonzalez Martin 2023-11-19 09:22:39 -06:00
parent 20fe6c8f97
commit df53762d92
11 changed files with 1774 additions and 1618 deletions

View File

@ -5,14 +5,18 @@ pub fn build(b: *std.Build) !void {
all = b.option(bool, "all", "All") orelse false; all = b.option(bool, "all", "All") orelse false;
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimization = b.standardOptimizeOption(.{}); const optimization = b.standardOptimizeOption(.{});
const use_llvm = b.option(bool, "use_llvm", "Use LLVM as the backend for generate the compiler binary") orelse true;
const exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "nativity", .name = "nativity",
.root_source_file = .{ .path = "src/main.zig" }, .root_source_file = .{ .path = "src/main.zig" },
.target = target, .target = target,
.optimize = optimization, .optimize = optimization,
.use_llvm = true, .use_llvm = use_llvm,
.use_lld = false, .use_lld = false,
}); });
exe.unwind_tables = false;
exe.omit_frame_pointer = false;
b.installArtifact(exe); b.installArtifact(exe);
b.installDirectory(.{ b.installDirectory(.{
.source_dir = std.Build.LazyPath.relative("lib"), .source_dir = std.Build.LazyPath.relative("lib"),
@ -31,6 +35,8 @@ pub fn build(b: *std.Build) !void {
const debug_command = switch (@import("builtin").os.tag) { const debug_command = switch (@import("builtin").os.tag) {
.linux => blk: { .linux => blk: {
const result = b.addSystemCommand(&.{"gf2"}); const result = b.addSystemCommand(&.{"gf2"});
result.addArg("-ex=r");
result.addArgs(&.{ "-ex", "up" });
result.addArg("--args"); result.addArg("--args");
result.addArtifactArg(exe); result.addArtifactArg(exe);
break :blk result; break :blk result;

5
ci.sh
View File

@ -2,7 +2,8 @@
echo "Testing Nativity with Zig" echo "Testing Nativity with Zig"
echo "Compiling Nativity with Zig" echo "Compiling Nativity with Zig"
zig build nativity_use_llvm=false
zig build -Duse_llvm=$nativity_use_llvm
failed_test_count=0 failed_test_count=0
passed_test_count=0 passed_test_count=0
test_directory_name=test test_directory_name=test
@ -18,7 +19,7 @@ failed_tests=()
for dir in $test_directory for dir in $test_directory
do do
MY_TESTNAME=${dir##*/} MY_TESTNAME=${dir##*/}
zig build run -- $dir/main.nat zig build run -Duse_llvm=$nativity_use_llvm -- $dir/main.nat -log ir
if [[ "$?" == "0" ]]; then if [[ "$?" == "0" ]]; then
passed_compilation_count=$(($passed_compilation_count + 1)) passed_compilation_count=$(($passed_compilation_count + 1))
if [[ "$OSTYPE" == "linux-gnu"* ]]; then if [[ "$OSTYPE" == "linux-gnu"* ]]; then

View File

@ -53,6 +53,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
var maybe_executable_path: ?[]const u8 = null; var maybe_executable_path: ?[]const u8 = null;
var maybe_main_package_path: ?[]const u8 = null; var maybe_main_package_path: ?[]const u8 = null;
var target_triplet: []const u8 = "x86_64-linux-gnu"; var target_triplet: []const u8 = "x86_64-linux-gnu";
var transpile_to_c: ?bool = null;
var i: usize = 0; var i: usize = 0;
while (i < arguments.len) : (i += 1) { while (i < arguments.len) : (i += 1) {
@ -112,6 +113,21 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
} else { } else {
reportUnterminatedArgumentError(current_argument); reportUnterminatedArgumentError(current_argument);
} }
} else if (equal(u8, current_argument, "-transpile_to_c")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
if (std.mem.eql(u8, arg, "true")) {
transpile_to_c = true;
} else if (std.mem.equal(u8, arg, "false")) {
transpile_to_c = false;
} else {
unreachable;
}
} else {
reportUnterminatedArgumentError(current_argument);
}
} else { } else {
maybe_main_package_path = current_argument; maybe_main_package_path = current_argument;
} }
@ -133,6 +149,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
.main_package_path = main_package_path, .main_package_path = main_package_path,
.executable_path = executable_path, .executable_path = executable_path,
.target = target, .target = target,
.transpile_to_c = transpile_to_c orelse true,
}; };
} }
@ -236,7 +253,7 @@ pub const Type = union(enum) {
pub fn getIndex(integer: Integer) Compilation.Type.Index { pub fn getIndex(integer: Integer) Compilation.Type.Index {
return .{ return .{
.block = 0, .block = 0,
.index = @ctz(integer.bit_count) - @ctz(@as(u8, 8)) + @as(u6, switch (integer.signedness) { .element = @ctz(integer.bit_count) - @ctz(@as(u8, 8)) + @as(u6, switch (integer.signedness) {
.signed => Compilation.HardwareSignedIntegerType.offset, .signed => Compilation.HardwareSignedIntegerType.offset,
.unsigned => Compilation.HardwareUnsignedIntegerType.offset, .unsigned => Compilation.HardwareUnsignedIntegerType.offset,
}), }),
@ -287,26 +304,6 @@ pub const Type = union(enum) {
}; };
// Each time an enum is added here, a corresponding insertion in the initialization must be made // Each time an enum is added here, a corresponding insertion in the initialization must be made
pub const Values = enum {
bool_false,
bool_true,
@"unreachable",
pub fn getIndex(value: Values) Value.Index {
const absolute: u32 = @intFromEnum(value);
const foo = @as(Value.Index, undefined);
const ElementT = @TypeOf(@field(foo, "index"));
const BlockT = @TypeOf(@field(foo, "block"));
const divider = std.math.maxInt(ElementT);
const element_index: ElementT = @intCast(absolute % divider);
const block_index: BlockT = @intCast(absolute / divider);
return .{
.index = element_index,
.block = block_index,
};
}
};
pub const Intrinsic = enum { pub const Intrinsic = enum {
@"error", @"error",
import, import,
@ -556,6 +553,7 @@ pub const BinaryOperation = struct {
divide, divide,
shift_left, shift_left,
shift_right, shift_right,
compare_equal,
}; };
}; };
@ -563,6 +561,17 @@ pub const CallingConvention = enum {
system_v, system_v,
}; };
pub const Branch = struct {
condition: Value.Index,
true_expression: Value.Index,
false_expression: Value.Index,
reaches_end: bool,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Value = union(enum) { pub const Value = union(enum) {
unresolved: Unresolved, unresolved: Unresolved,
declaration: Declaration.Index, declaration: Declaration.Index,
@ -572,7 +581,8 @@ pub const Value = union(enum) {
undefined, undefined,
@"unreachable", @"unreachable",
loop: Loop.Index, loop: Loop.Index,
function: Function.Index, function_definition: Function.Index,
function_declaration: Function.Index,
block: Block.Index, block: Block.Index,
runtime: Runtime, runtime: Runtime,
assign: Assignment.Index, assign: Assignment.Index,
@ -589,6 +599,7 @@ pub const Value = union(enum) {
sign_extend: Cast.Index, sign_extend: Cast.Index,
zero_extend: Cast.Index, zero_extend: Cast.Index,
binary_operation: BinaryOperation.Index, binary_operation: BinaryOperation.Index,
branch: Branch.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
@ -606,7 +617,7 @@ pub const Value = union(enum) {
pub fn isComptime(value: *Value, module: *Module) bool { pub fn isComptime(value: *Value, module: *Module) bool {
return switch (value.*) { return switch (value.*) {
.bool, .void, .undefined, .function, .type, .enum_field => true, .bool, .void, .undefined, .function_definition, .type, .enum_field => true,
.integer => |integer| integer.type.eq(Type.comptime_int), .integer => |integer| integer.type.eq(Type.comptime_int),
.call => false, .call => false,
.binary_operation => false, .binary_operation => false,
@ -623,8 +634,11 @@ pub const Value = union(enum) {
.string_literal => |string_literal_hash| module.string_literal_types.get(@intCast(module.getStringLiteral(string_literal_hash).?.len)).?, .string_literal => |string_literal_hash| module.string_literal_types.get(@intCast(module.getStringLiteral(string_literal_hash).?.len)).?,
.type => Type.type, .type => Type.type,
.enum_field => |enum_field_index| module.enums.get(module.enum_fields.get(enum_field_index).parent).type, .enum_field => |enum_field_index| module.enums.get(module.enum_fields.get(enum_field_index).parent).type,
.function => |function_index| module.functions.get(function_index).prototype, .function_definition => |function_index| module.function_definitions.get(function_index).prototype,
.function_declaration => |function_index| module.function_declarations.get(function_index).prototype,
.binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type, .binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type,
.bool => Type.boolean,
.declaration => Type.void,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -703,9 +717,10 @@ pub const Module = struct {
scopes: BlockList(Scope) = .{}, scopes: BlockList(Scope) = .{},
files: BlockList(File) = .{}, files: BlockList(File) = .{},
values: BlockList(Value) = .{}, values: BlockList(Value) = .{},
functions: BlockList(Function) = .{}, function_definitions: BlockList(Function) = .{},
fields: BlockList(Field) = .{}, function_declarations: BlockList(Function) = .{},
function_prototypes: BlockList(Function.Prototype) = .{}, function_prototypes: BlockList(Function.Prototype) = .{},
fields: BlockList(Field) = .{},
types: BlockList(Type) = .{}, types: BlockList(Type) = .{},
blocks: BlockList(Block) = .{}, blocks: BlockList(Block) = .{},
loops: BlockList(Loop) = .{}, loops: BlockList(Loop) = .{},
@ -721,6 +736,7 @@ pub const Module = struct {
arrays: BlockList(Array) = .{}, arrays: BlockList(Array) = .{},
casts: BlockList(Cast) = .{}, casts: BlockList(Cast) = .{},
binary_operations: BlockList(BinaryOperation) = .{}, binary_operations: BlockList(BinaryOperation) = .{},
branches: BlockList(Branch) = .{},
string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{}, string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{},
array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{}, array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{},
entry_point: Function.Index = Function.Index.invalid, entry_point: Function.Index = Function.Index.invalid,
@ -729,6 +745,7 @@ pub const Module = struct {
main_package_path: []const u8, main_package_path: []const u8,
executable_path: []const u8, executable_path: []const u8,
target: std.Target, target: std.Target,
transpile_to_c: bool,
}; };
const ImportFileResult = struct { const ImportFileResult = struct {
@ -1047,17 +1064,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
_ = try module.types.append(compilation.base_allocator, type_data); _ = try module.types.append(compilation.base_allocator, type_data);
} }
_ = try module.values.append(compilation.base_allocator, .{ semantic_analyzer.unreachable_index = (try module.values.append(compilation.base_allocator, .@"unreachable")).index;
.bool = false,
});
_ = try module.values.append(compilation.base_allocator, .{
.bool = true,
});
_ = try module.values.append(compilation.base_allocator, .{
.@"unreachable" = {},
});
const value_allocation = try module.values.append(compilation.base_allocator, .{ const value_allocation = try module.values.append(compilation.base_allocator, .{
.unresolved = .{ .unresolved = .{
@ -1196,13 +1203,14 @@ pub fn log(comptime logger_scope: LoggerScope, logger: getLoggerScopeType(logger
} }
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_address: ?usize) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_address: ?usize) noreturn {
const print_stack_trace = true; const print_stack_trace = false;
switch (print_stack_trace) { switch (print_stack_trace) {
true => @call(.always_inline, std.builtin.default_panic, .{ message, stack_trace, return_address }), true => @call(.always_inline, std.builtin.default_panic, .{ message, stack_trace, return_address }),
false => { false => {
writer.writeAll("\nPANIC: ") catch {}; writer.writeAll("\nPANIC: ") catch {};
writer.writeAll(message) catch {}; writer.writeAll(message) catch {};
writer.writeByte('\n') catch {}; writer.writeByte('\n') catch {};
@breakpoint();
std.os.abort(); std.os.abort();
}, },
} }

View File

@ -9,6 +9,7 @@ const expectEqual = std.testing.expectEqual;
const Compilation = @import("../Compilation.zig"); const Compilation = @import("../Compilation.zig");
const ir = @import("intermediate_representation.zig"); const ir = @import("intermediate_representation.zig");
const IR = ir.IR;
const data_structures = @import("../data_structures.zig"); const data_structures = @import("../data_structures.zig");
const ArrayList = data_structures.ArrayList; const ArrayList = data_structures.ArrayList;
@ -234,7 +235,7 @@ pub fn get(comptime arch: std.Target.Cpu.Arch) type {
}; };
return struct { return struct {
pub fn initialize(allocator: Allocator, intermediate: *ir.Result, descriptor: Compilation.Module.Descriptor) !void { pub fn initialize(allocator: Allocator, intermediate: *IR, descriptor: Compilation.Module.Descriptor) !void {
switch (arch) { switch (arch) {
.x86_64 => { .x86_64 => {
var mir = try backend.MIR.selectInstructions(allocator, intermediate, descriptor.target); var mir = try backend.MIR.selectInstructions(allocator, intermediate, descriptor.target);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ pub fn BlockList(comptime T: type) type {
}; };
return struct { return struct {
// TODO: make this not reallocate the whole block. Instead, use a pointer to the block as the ArrayList item
blocks: ArrayList(Block) = .{}, blocks: ArrayList(Block) = .{},
len: usize = 0, len: usize = 0,
first_block: u32 = 0, first_block: u32 = 0,
@ -38,14 +39,14 @@ pub fn BlockList(comptime T: type) type {
const List = @This(); const List = @This();
pub const Index = packed struct(u32) { pub const Index = packed struct(u32) {
index: u6, element: u6,
block: u24, block: u24,
_reserved: bool = false, _reserved: bool = false,
invalid: bool = false, invalid: bool = false,
pub const invalid = Index{ pub const invalid = Index{
.invalid = true, .invalid = true,
.index = 0, .element = 0,
.block = 0, .block = 0,
}; };
@ -63,42 +64,50 @@ pub fn BlockList(comptime T: type) type {
const block: u24 = @intCast(index / item_count); const block: u24 = @intCast(index / item_count);
const i: u6 = @intCast(index % item_count); const i: u6 = @intCast(index % item_count);
return .{ return .{
.index = i, .element = i,
.block = block, .block = block,
}; };
} }
}; };
pub const Iterator = struct { pub const Iterator = struct {
block_index: u24, index: Index,
element_index: u6, list: *List,
list: *const List,
pub fn getCurrentIndex(i: *const Iterator) Index { pub const Pair = struct {
return .{ index: Index,
.block = i.block_index, };
.index = @intCast(i.element_index),
};
}
pub fn next(i: *Iterator) ?T { pub fn nextIndex(i: *Iterator) ?Index {
return if (i.nextPointer()) |ptr| ptr.* else null; // TODO: optimize with ctz and masking out already iterated indices in the bitmask
} for (i.index.block..i.list.blocks.items.len) |block_index| {
for (@as(u8, i.index.element)..item_count) |element_index| {
if (i.list.blocks.items[block_index].bitset.isSet(element_index)) {
const index = Index{
.element = @intCast(element_index),
.block = @intCast(block_index),
};
pub fn nextPointer(i: *Iterator) ?*T { i.index = index;
for (i.block_index..i.list.blocks.items.len) |block_index| { i.index.element +%= 1;
for (@as(u8, i.element_index)..item_count) |element_index| { i.index.block = @as(u24, @intCast(block_index)) + @intFromBool(i.index.element < element_index);
if (i.list.blocks.items[i.block_index].bitset.isSet(element_index)) {
i.element_index = @intCast(element_index); return index;
i.element_index +%= 1;
i.block_index = @as(u24, @intCast(block_index)) + @intFromBool(i.element_index < element_index);
return &i.list.blocks.items[block_index].items[element_index];
} }
} }
} }
return null; return null;
} }
pub fn nextPointer(i: *Iterator) ?*T {
if (i.nextIndex()) |index| {
const result = i.list.get(index);
return result;
} else {
return null;
}
}
}; };
pub const Allocation = struct { pub const Allocation = struct {
@ -106,17 +115,19 @@ pub fn BlockList(comptime T: type) type {
index: Index, index: Index,
}; };
pub fn iterator(list: *const List) Iterator { pub fn iterator(list: *List) Iterator {
return .{ return .{
.block_index = 0, .index = Index{
.element_index = 0, .element = 0,
.block = 0,
},
.list = list, .list = list,
}; };
} }
pub fn get(list: *List, index: Index) *T { pub fn get(list: *List, index: Index) *T {
assert(!index.invalid); assert(!index.invalid);
return &list.blocks.items[index.block].items[index.index]; return &list.blocks.items[index.block].items[index.element];
} }
pub fn append(list: *List, allocator: Allocator, element: T) !Allocation { pub fn append(list: *List, allocator: Allocator, element: T) !Allocation {
@ -131,12 +142,12 @@ pub fn BlockList(comptime T: type) type {
const result = switch (list.len < max_allocation) { const result = switch (list.len < max_allocation) {
true => blk: { true => blk: {
const block = &list.blocks.items[list.first_block]; const block = &list.blocks.items[list.first_block];
if (block.allocateIndex()) |index| { if (block.allocateIndex()) |element_index| {
const ptr = &block.items[index]; const ptr = &block.items[element_index];
break :blk Allocation{ break :blk Allocation{
.ptr = ptr, .ptr = ptr,
.index = .{ .index = .{
.index = index, .element = element_index,
.block = @intCast(list.first_block), .block = @intCast(list.first_block),
}, },
}; };
@ -148,13 +159,13 @@ pub fn BlockList(comptime T: type) type {
const block_index = list.blocks.items.len; const block_index = list.blocks.items.len;
const new_block = list.blocks.addOneAssumeCapacity(); const new_block = list.blocks.addOneAssumeCapacity();
new_block.* = .{}; new_block.* = .{};
const index = new_block.allocateIndex() catch unreachable; const element_index = new_block.allocateIndex() catch unreachable;
const ptr = &new_block.items[index]; const ptr = &new_block.items[element_index];
list.first_block += @intFromBool(block_index != 0); list.first_block += @intFromBool(block_index != 0);
break :blk Allocation{ break :blk Allocation{
.ptr = ptr, .ptr = ptr,
.index = .{ .index = .{
.index = index, .element = element_index,
.block = @intCast(block_index), .block = @intCast(block_index),
}, },
}; };
@ -174,7 +185,7 @@ pub fn BlockList(comptime T: type) type {
} }
} }
pub fn indexOf(list: *const List, elem: *const T) Index { pub fn indexOf(list: *List, elem: *const T) Index {
const address = @intFromPtr(elem); const address = @intFromPtr(elem);
for (list.blocks.items, 0..) |*block, block_index| { for (list.blocks.items, 0..) |*block, block_index| {
const base = @intFromPtr(&block.items[0]); const base = @intFromPtr(&block.items[0]);
@ -182,7 +193,7 @@ pub fn BlockList(comptime T: type) type {
if (address >= base and address < top) { if (address >= base and address < top) {
return .{ return .{
.block = @intCast(block_index), .block = @intCast(block_index),
.index = @intCast(@divExact(address - base, @sizeOf(T))), .element = @intCast(@divExact(address - base, @sizeOf(T))),
}; };
} }
} }

View File

@ -146,6 +146,7 @@ const Analyzer = struct {
var reaches_end = true; var reaches_end = true;
const block_node = analyzer.getScopeNode(scope_index, node_index); const block_node = analyzer.getScopeNode(scope_index, node_index);
var statement_nodes = ArrayList(Node.Index){}; var statement_nodes = ArrayList(Node.Index){};
switch (block_node.id) { switch (block_node.id) {
.block_one, .comptime_block_one => { .block_one, .comptime_block_one => {
try statement_nodes.append(analyzer.allocator, block_node.left); try statement_nodes.append(analyzer.allocator, block_node.left);
@ -175,7 +176,7 @@ const Analyzer = struct {
} }
const statement_node = analyzer.getScopeNode(scope_index, statement_node_index); const statement_node = analyzer.getScopeNode(scope_index, statement_node_index);
const statement_value = switch (statement_node.id) { const statement_value_index = switch (statement_node.id) {
.assign => (try analyzer.module.values.append(analyzer.allocator, try analyzer.processAssignment(scope_index, statement_node_index))).index, .assign => (try analyzer.module.values.append(analyzer.allocator, try analyzer.processAssignment(scope_index, statement_node_index))).index,
.simple_while => blk: { .simple_while => blk: {
const loop_allocation = try analyzer.module.loops.append(analyzer.allocator, .{ const loop_allocation = try analyzer.module.loops.append(analyzer.allocator, .{
@ -196,7 +197,7 @@ const Analyzer = struct {
}, },
.@"unreachable" => blk: { .@"unreachable" => blk: {
reaches_end = false; reaches_end = false;
break :blk Compilation.Values.@"unreachable".getIndex(); break :blk unreachable_index;
}, },
.simple_symbol_declaration => blk: { .simple_symbol_declaration => blk: {
const declaration_index = try analyzer.symbolDeclaration(scope_index, statement_node_index, .local); const declaration_index = try analyzer.symbolDeclaration(scope_index, statement_node_index, .local);
@ -216,18 +217,43 @@ const Analyzer = struct {
.@"return" => blk: { .@"return" => blk: {
reaches_end = false; reaches_end = false;
const return_value_allocation = try analyzer.module.values.append(analyzer.allocator, try analyzer.processReturn(scope_index, expect_type, statement_node_index)); const return_expresssion = try analyzer.processReturn(scope_index, expect_type, statement_node_index);
const return_value_allocation = try analyzer.module.values.append(analyzer.allocator, return_expresssion);
break :blk return_value_allocation.index; break :blk return_value_allocation.index;
}, },
.call_two, .call => (try analyzer.module.values.append(analyzer.allocator, .{ .call_two, .call => blk: {
.call = try analyzer.processCall(scope_index, statement_node_index), const call_index = try analyzer.processCall(scope_index, statement_node_index);
})).index, const call_statement = try analyzer.module.values.append(analyzer.allocator, .{
.@"switch" => (try analyzer.module.values.append(analyzer.allocator, try analyzer.processSwitch(scope_index, statement_node_index))).index, .call = call_index,
});
if (call_statement.ptr.getType(analyzer.module).eq(Type.noreturn)) {
reaches_end = false;
}
break :blk call_statement.index;
},
// TODO: reaches end switch statement
.@"switch" => blk: {
const switch_value = try analyzer.processSwitch(scope_index, expect_type, statement_node_index);
switch (switch_value) {
.@"return" => reaches_end = false,
else => {},
}
const switch_value_allocation = try analyzer.module.values.append(analyzer.allocator, switch_value);
break :blk switch_value_allocation.index;
},
.if_else => blk: {
const if_else_value = try analyzer.processIfElse(scope_index, expect_type, statement_node_index);
const branch = analyzer.module.branches.get(if_else_value.branch);
reaches_end = branch.reaches_end;
const branch_statement = try analyzer.module.values.append(analyzer.allocator, if_else_value);
break :blk branch_statement.index;
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
try statements.append(analyzer.allocator, statement_value); try statements.append(analyzer.allocator, statement_value_index);
} }
const block_allocation = try analyzer.module.blocks.append(analyzer.allocator, .{ const block_allocation = try analyzer.module.blocks.append(analyzer.allocator, .{
@ -254,7 +280,7 @@ const Analyzer = struct {
const left_type = switch (left_value_index.invalid) { const left_type = switch (left_value_index.invalid) {
false => switch (analyzer.module.values.get(left_value_index).*) { false => switch (analyzer.module.values.get(left_value_index).*) {
.function => |function_index| analyzer.module.function_prototypes.get(analyzer.module.types.get(analyzer.module.functions.get(function_index).prototype).function).return_type, .function_definition => |function_index| analyzer.module.function_prototypes.get(analyzer.module.types.get(analyzer.module.function_definitions.get(function_index).prototype).function).return_type,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}, },
true => Type.Index.invalid, true => Type.Index.invalid,
@ -270,9 +296,9 @@ const Analyzer = struct {
}; };
switch (analyzer.module.values.get(left_value_index).*) { switch (analyzer.module.values.get(left_value_index).*) {
.function => |function_index| { .function_definition => |function_index| {
const function = analyzer.module.functions.get(function_index); const function_definition = analyzer.module.function_definitions.get(function_index);
const function_prototype = analyzer.module.function_prototypes.get(analyzer.module.types.get(function.prototype).function); const function_prototype = analyzer.module.function_prototypes.get(analyzer.module.types.get(function_definition.prototype).function);
const argument_declarations = function_prototype.arguments.?; const argument_declarations = function_prototype.arguments.?;
logln(.sema, .call, "Argument declaration count: {}. Argument node list count: {}\n", .{ argument_declarations.len, call_argument_node_list.len }); logln(.sema, .call, "Argument declaration count: {}. Argument node list count: {}\n", .{ argument_declarations.len, call_argument_node_list.len });
var argument_array = ArrayList(Value.Index){}; var argument_array = ArrayList(Value.Index){};
@ -340,7 +366,8 @@ const Analyzer = struct {
} }
} }
fn processSwitch(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Value { fn processSwitch(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value {
_ = expect_type;
const node = analyzer.getScopeNode(scope_index, node_index); const node = analyzer.getScopeNode(scope_index, node_index);
assert(node.id == .@"switch"); assert(node.id == .@"switch");
@ -458,6 +485,44 @@ const Analyzer = struct {
unreachable; unreachable;
} }
fn processIfElse(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value {
const node = analyzer.getScopeNode(scope_index, node_index);
assert(node.id == .if_else);
assert(!node.left.invalid);
assert(!node.right.invalid);
const if_branch_node = analyzer.getScopeNode(scope_index, node.left);
const if_condition = try analyzer.unresolvedAllocate(scope_index, ExpectType.boolean, if_branch_node.left);
switch (if_condition.ptr.*) {
.declaration_reference => {
const true_expression = try analyzer.unresolvedAllocate(scope_index, expect_type, if_branch_node.right);
const true_reaches_end = switch (true_expression.ptr.*) {
.block => |block_index| analyzer.module.blocks.get(block_index).reaches_end,
else => |t| @panic(@tagName(t)),
};
const false_expression = try analyzer.unresolvedAllocate(scope_index, expect_type, node.right);
const false_reaches_end = switch (true_expression.ptr.*) {
.block => |block_index| analyzer.module.blocks.get(block_index).reaches_end,
else => |t| @panic(@tagName(t)),
};
const reaches_end = true_reaches_end and false_reaches_end;
const branch = try analyzer.module.branches.append(analyzer.allocator, .{
.condition = if_condition.index,
.true_expression = true_expression.index,
.false_expression = false_expression.index,
.reaches_end = reaches_end,
});
return Value{
.branch = branch.index,
};
},
.bool => unreachable,
else => |t| @panic(@tagName(t)),
}
}
fn processAssignment(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Value { fn processAssignment(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Value {
const node = analyzer.getScopeNode(scope_index, node_index); const node = analyzer.getScopeNode(scope_index, node_index);
assert(node.id == .assign); assert(node.id == .assign);
@ -536,9 +601,15 @@ const Analyzer = struct {
.divide => .divide, .divide => .divide,
.shift_left => .shift_left, .shift_left => .shift_left,
.shift_right => .shift_right, .shift_right => .shift_right,
.compare_equal => .compare_equal,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
const left_expect_type: ExpectType = switch (binary_operation_id) {
.compare_equal => ExpectType.none,
else => expect_type,
};
const left_allocation = try analyzer.unresolvedAllocate(scope_index, left_expect_type, node.left);
const right_expect_type: ExpectType = switch (binary_operation_id) { const right_expect_type: ExpectType = switch (binary_operation_id) {
.add, .add,
.sub, .sub,
@ -553,18 +624,26 @@ const Analyzer = struct {
=> ExpectType{ => ExpectType{
.type_index = Type.u8, .type_index = Type.u8,
}, },
.compare_equal => ExpectType{
.type_index = left_allocation.ptr.getType(analyzer.module),
},
}; };
const left_allocation = try analyzer.unresolvedAllocate(scope_index, expect_type, node.left);
const right_allocation = try analyzer.unresolvedAllocate(scope_index, right_expect_type, node.right); const right_allocation = try analyzer.unresolvedAllocate(scope_index, right_expect_type, node.right);
const left_type = left_allocation.ptr.getType(analyzer.module); const left_type = left_allocation.ptr.getType(analyzer.module);
const right_type = right_allocation.ptr.getType(analyzer.module); // const right_type = right_allocation.ptr.getType(analyzer.module);
_ = right_type; // _ = right_type;
const binary_operation = try analyzer.module.binary_operations.append(analyzer.allocator, .{ const binary_operation = try analyzer.module.binary_operations.append(analyzer.allocator, .{
.left = left_allocation.index, .left = left_allocation.index,
.right = right_allocation.index, .right = right_allocation.index,
.type = left_type, .type = switch (expect_type) {
.none => switch (binary_operation_id) {
.logical_and => left_type,
else => |t| @panic(@tagName(t)),
},
.type_index => |type_index| type_index,
else => |t| @panic(@tagName(t)),
},
.id = binary_operation_id, .id = binary_operation_id,
}); });
@ -615,7 +694,7 @@ const Analyzer = struct {
try analyzer.resolveNode(init_value, lookup.scope, expect_type, init_value.unresolved.node_index); try analyzer.resolveNode(init_value, lookup.scope, expect_type, init_value.unresolved.node_index);
declaration.type = init_value.getType(analyzer.module); declaration.type = init_value.getType(analyzer.module);
switch (init_value.*) { switch (init_value.*) {
.function => |function_index| { .function_definition => |function_index| {
try analyzer.module.function_name_map.put(analyzer.allocator, function_index, declaration.name); try analyzer.module.function_name_map.put(analyzer.allocator, function_index, declaration.name);
}, },
else => {}, else => {},
@ -721,7 +800,7 @@ const Analyzer = struct {
const value_ref = analyzer.module.values.get(value_index); const value_ref = analyzer.module.values.get(value_index);
break :blk value_ref.*; break :blk value_ref.*;
}, },
.keyword_true => { .keyword_true, .keyword_false => blk: {
switch (expect_type) { switch (expect_type) {
.none => {}, .none => {},
.type_index => |expected_type| { .type_index => |expected_type| {
@ -732,10 +811,13 @@ const Analyzer = struct {
else => unreachable, else => unreachable,
} }
// TODO break :blk .{
unreachable; .bool = switch (node.id) {
.keyword_true => true,
// break :blk Values.getIndex(.bool_true); .keyword_false => false,
else => unreachable,
},
};
}, },
.compiler_intrinsic_one, .compiler_intrinsic_two, .compiler_intrinsic => blk: { .compiler_intrinsic_one, .compiler_intrinsic_two, .compiler_intrinsic => blk: {
const intrinsic_name = analyzer.tokenIdentifier(scope_index, node.token + 1); const intrinsic_name = analyzer.tokenIdentifier(scope_index, node.token + 1);
@ -826,6 +908,8 @@ const Analyzer = struct {
}); });
const function_prototype_index = try analyzer.functionPrototype(function_scope_allocation.index, node.left); const function_prototype_index = try analyzer.functionPrototype(function_scope_allocation.index, node.left);
const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index);
assert(!function_prototype.attributes.@"extern");
const function_body = try analyzer.block(function_scope_allocation.index, .{ const function_body = try analyzer.block(function_scope_allocation.index, .{
.type_index = analyzer.functionPrototypeReturnType(function_prototype_index), .type_index = analyzer.functionPrototypeReturnType(function_prototype_index),
@ -835,14 +919,14 @@ const Analyzer = struct {
.function = function_prototype_index, .function = function_prototype_index,
}); });
const function_allocation = try analyzer.module.functions.append(analyzer.allocator, .{ const function_allocation = try analyzer.module.function_definitions.append(analyzer.allocator, .{
.prototype = prototype_type.index, .prototype = prototype_type.index,
.body = function_body, .body = function_body,
.scope = function_scope_allocation.index, .scope = function_scope_allocation.index,
}); });
break :blk .{ break :blk .{
.function = function_allocation.index, .function_definition = function_allocation.index,
}; };
}, },
.function_prototype => blk: { .function_prototype => blk: {
@ -854,13 +938,13 @@ const Analyzer = struct {
const prototype_type = try analyzer.module.types.append(analyzer.allocator, .{ const prototype_type = try analyzer.module.types.append(analyzer.allocator, .{
.function = function_prototype_index, .function = function_prototype_index,
}); });
const function_allocation = try analyzer.module.functions.append(analyzer.allocator, .{ const function_declaration = try analyzer.module.function_declarations.append(analyzer.allocator, .{
.prototype = prototype_type.index, .prototype = prototype_type.index,
.body = Block.Index.invalid, .body = Block.Index.invalid,
.scope = Scope.Index.invalid, .scope = Scope.Index.invalid,
}); });
break :b .{ break :b .{
.function = function_allocation.index, .function_declaration = function_declaration.index,
}; };
}, },
false => unreachable, false => unreachable,
@ -919,7 +1003,7 @@ const Analyzer = struct {
const right_index = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index); const right_index = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index);
const right_value = analyzer.module.values.get(right_index); const right_value = analyzer.module.values.get(right_index);
switch (right_value.*) { switch (right_value.*) {
.function, .type, .enum_field => break :blk right_value.*, .function_definition, .type, .enum_field => break :blk right_value.*,
.declaration_reference => |declaration_reference| { .declaration_reference => |declaration_reference| {
const declaration = analyzer.module.declarations.get(declaration_reference.value); const declaration = analyzer.module.declarations.get(declaration_reference.value);
const declaration_name = analyzer.module.getName(declaration.name).?; const declaration_name = analyzer.module.getName(declaration.name).?;
@ -983,7 +1067,7 @@ const Analyzer = struct {
.string_literal => .{ .string_literal => .{
.string_literal = try analyzer.processStringLiteral(scope_index, node_index), .string_literal = try analyzer.processStringLiteral(scope_index, node_index),
}, },
.@"switch" => try analyzer.processSwitch(scope_index, node_index), .@"switch" => try analyzer.processSwitch(scope_index, expect_type, node_index),
.enum_type => blk: { .enum_type => blk: {
const list_node = analyzer.getScopeNode(scope_index, node.left); const list_node = analyzer.getScopeNode(scope_index, node.left);
const field_node_list = switch (list_node.id) { const field_node_list = switch (list_node.id) {
@ -1038,6 +1122,7 @@ const Analyzer = struct {
.divide, .divide,
.shift_left, .shift_left,
.shift_right, .shift_right,
.compare_equal,
=> try analyzer.processBinaryOperation(scope_index, expect_type, node_index), => try analyzer.processBinaryOperation(scope_index, expect_type, node_index),
.expression_group => return try analyzer.resolveNode(value, scope_index, expect_type, node.left), //unreachable, .expression_group => return try analyzer.resolveNode(value, scope_index, expect_type, node.left), //unreachable,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -1146,6 +1231,7 @@ const Analyzer = struct {
.void_type => Type.void, .void_type => Type.void,
.ssize_type => Type.ssize, .ssize_type => Type.ssize,
.usize_type => Type.usize, .usize_type => Type.usize,
.bool_type => Type.boolean,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
return type_index; return type_index;
@ -1525,6 +1611,9 @@ const Analyzer = struct {
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}, },
.bool => switch (source_type.*) {
else => |t| @panic(@tagName(t)),
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
}, },
@ -1584,6 +1673,8 @@ const ExpectType = union(enum) {
}; };
}; };
pub var unreachable_index = Value.Index.invalid;
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_value: *Value) !void { pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_value: *Value) !void {
_ = try analyzeExistingPackage(main_value, compilation, module, package); _ = try analyzeExistingPackage(main_value, compilation, module, package);
@ -1593,7 +1684,7 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package,
if (equal(u8, declaration_name, "_start")) { if (equal(u8, declaration_name, "_start")) {
const value = module.values.get(decl.init_value); const value = module.values.get(decl.init_value);
module.entry_point = switch (value.*) { module.entry_point = switch (value.*) {
.function => |function_index| function_index, .function_definition => |function_index| function_index,
.unresolved => panic("Unresolved declaration: {s}\n", .{declaration_name}), .unresolved => panic("Unresolved declaration: {s}\n", .{declaration_name}),
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };

View File

@ -162,6 +162,7 @@ pub const Node = packed struct(u128) {
divide = 68, divide = 68,
shift_left = 69, shift_left = 69,
shift_right = 70, shift_right = 70,
bool_type = 71,
}; };
}; };
@ -611,7 +612,7 @@ const Analyzer = struct {
analyzer.token_i += 1; analyzer.token_i += 1;
_ = try analyzer.expectToken(.left_parenthesis); _ = try analyzer.expectToken(.left_parenthesis);
const if_expression = try analyzer.expression(); const if_condition = try analyzer.expression();
_ = try analyzer.expectToken(.right_parenthesis); _ = try analyzer.expectToken(.right_parenthesis);
const if_block = try analyzer.block(.{ .is_comptime = false }); const if_block = try analyzer.block(.{ .is_comptime = false });
@ -619,7 +620,7 @@ const Analyzer = struct {
const if_node = try analyzer.addNode(.{ const if_node = try analyzer.addNode(.{
.id = .@"if", .id = .@"if",
.token = if_token, .token = if_token,
.left = if_expression, .left = if_condition,
.right = if_block, .right = if_block,
}); });
@ -782,7 +783,6 @@ const Analyzer = struct {
const token = analyzer.tokens[analyzer.token_i]; const token = analyzer.tokens[analyzer.token_i];
// logln("Looping in expression precedence with token {}\n", .{token}); // logln("Looping in expression precedence with token {}\n", .{token});
const operator: PrecedenceOperator = switch (token.id) { const operator: PrecedenceOperator = switch (token.id) {
.equal,
.semicolon, .semicolon,
.right_parenthesis, .right_parenthesis,
.right_brace, .right_brace,
@ -1180,6 +1180,15 @@ const Analyzer = struct {
.right = Node.Index.invalid, .right = Node.Index.invalid,
}), }),
.hash => analyzer.compilerIntrinsic(), .hash => analyzer.compilerIntrinsic(),
.fixed_keyword_bool => analyzer.addNode(.{
.id = .bool_type,
.token = blk: {
analyzer.token_i += 1;
break :blk token_i;
},
.left = Node.Index.invalid,
.right = Node.Index.invalid,
}),
.keyword_unsigned_integer, .keyword_signed_integer => |signedness| analyzer.addNode(.{ .keyword_unsigned_integer, .keyword_signed_integer => |signedness| analyzer.addNode(.{
.id = switch (signedness) { .id = switch (signedness) {
.keyword_unsigned_integer => .unsigned_integer_type, .keyword_unsigned_integer => .unsigned_integer_type,

View File

@ -6,6 +6,7 @@ pub const panic = Compilation.panic;
pub fn main() !void { pub fn main() !void {
const allocator = std.heap.page_allocator; const allocator = std.heap.page_allocator;
try Compilation.init(allocator); try Compilation.init(allocator);
} }

View File

@ -0,0 +1,8 @@
const main = fn () s32 {
var false_boolean: bool = false;
if (false_boolean) {
return 1;
} else {
return 0;
}
}