sema for main function

This commit is contained in:
David Gonzalez Martin 2023-09-28 21:57:43 -06:00
parent bf07777ff7
commit 611e611cab
10 changed files with 872 additions and 507 deletions

View File

@ -3,6 +3,7 @@ comptime {
} }
const _start = fn () noreturn { const _start = fn () noreturn {
_ = #syscall(231, 0); const result = #import("main").main();
_ = #syscall(231, result);
unreachable; unreachable;
}; };

View File

@ -64,6 +64,7 @@ pub const Struct = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Type = union(enum) { pub const Type = union(enum) {
@ -74,6 +75,7 @@ pub const Type = union(enum) {
@"struct": Struct.Index, @"struct": Struct.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Integer = struct { pub const Integer = struct {
@ -87,12 +89,14 @@ pub const Integer = struct {
/// A scope contains a bunch of declarations /// A scope contains a bunch of declarations
pub const Scope = struct { pub const Scope = struct {
parent: Scope.Index,
type: Type.Index = Type.Index.invalid,
declarations: AutoHashMap(u32, Declaration.Index) = .{}, declarations: AutoHashMap(u32, Declaration.Index) = .{},
parent: Scope.Index,
file: File.Index,
type: Type.Index = Type.Index.invalid,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const ScopeType = enum(u1) { pub const ScopeType = enum(u1) {
@ -113,6 +117,7 @@ pub const Declaration = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Function = struct { pub const Function = struct {
@ -133,6 +138,7 @@ pub const Function = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Block = struct { pub const Block = struct {
@ -140,6 +146,7 @@ pub const Block = struct {
reaches_end: bool, reaches_end: bool,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Field = struct { pub const Field = struct {
@ -147,6 +154,7 @@ pub const Field = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Loop = struct { pub const Loop = struct {
@ -156,6 +164,7 @@ pub const Loop = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
const Runtime = struct { const Runtime = struct {
@ -172,6 +181,7 @@ pub const Assignment = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
}; };
pub const Syscall = struct { pub const Syscall = struct {
@ -185,11 +195,35 @@ pub const Syscall = struct {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Call = struct {
value: Value.Index,
arguments: ArgumentList.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const ArgumentList = struct {
array: ArrayList(Value.Index),
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Return = struct {
value: Value.Index,
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,
declaration_reference: Declaration.Index,
void, void,
bool: bool, bool: bool,
undefined, undefined,
@ -202,9 +236,13 @@ pub const Value = union(enum) {
type: Type.Index, type: Type.Index,
integer: u64, integer: u64,
syscall: Syscall.Index, syscall: Syscall.Index,
call: Call.Index,
argument_list: ArgumentList,
@"return": Return.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation;
pub fn isComptime(value: Value) bool { pub fn isComptime(value: Value) bool {
return switch (value) { return switch (value) {
@ -238,23 +276,26 @@ pub const Module = struct {
loops: BlockList(Loop) = .{}, loops: BlockList(Loop) = .{},
assignments: BlockList(Assignment) = .{}, assignments: BlockList(Assignment) = .{},
syscalls: BlockList(Syscall) = .{}, syscalls: BlockList(Syscall) = .{},
calls: BlockList(Call) = .{},
argument_list: BlockList(ArgumentList) = .{},
returns: BlockList(Return) = .{},
pub const Descriptor = struct { pub const Descriptor = struct {
main_package_path: []const u8, main_package_path: []const u8,
}; };
const ImportFileResult = struct { const ImportFileResult = struct {
file: *File, ptr: *File,
index: File.Index,
is_new: bool, is_new: bool,
}; };
const ImportPackageResult = struct { const ImportPackageResult = struct {
file: *File, file: ImportFileResult,
is_new: bool,
is_package: bool, is_package: bool,
}; };
pub fn importFile(module: *Module, allocator: Allocator, current_file: *File, import_name: []const u8) !ImportPackageResult { pub fn importFile(module: *Module, allocator: Allocator, current_file_index: File.Index, import_name: []const u8) !ImportPackageResult {
print("import: '{s}'\n", .{import_name}); print("import: '{s}'\n", .{import_name});
if (equal(u8, import_name, "std")) { if (equal(u8, import_name, "std")) {
return module.importPackage(allocator, module.main_package.dependencies.get("std").?); return module.importPackage(allocator, module.main_package.dependencies.get("std").?);
@ -268,6 +309,7 @@ pub const Module = struct {
return module.importPackage(allocator, module.main_package); return module.importPackage(allocator, module.main_package);
} }
const current_file = module.files.get(current_file_index);
if (current_file.package.dependencies.get(import_name)) |package| { if (current_file.package.dependencies.get(import_name)) |package| {
return module.importPackage(allocator, package); return module.importPackage(allocator, package);
} }
@ -279,55 +321,73 @@ pub const Module = struct {
const full_path = try std.fs.path.join(allocator, &.{ current_file.package.directory.path, import_name }); const full_path = try std.fs.path.join(allocator, &.{ current_file.package.directory.path, import_name });
const file_relative_path = std.fs.path.basename(full_path); const file_relative_path = std.fs.path.basename(full_path);
const package = current_file.package; const package = current_file.package;
const import = try module.getFile(allocator, full_path, file_relative_path, package); const import_file = try module.getFile(allocator, full_path, file_relative_path, package);
try import.file.addFileReference(allocator, current_file); try import_file.ptr.addFileReference(allocator, current_file);
const result = ImportPackageResult{ const result = ImportPackageResult{
.file = import.file, .file = import_file,
.is_new = import.is_new,
.is_package = false, .is_package = false,
}; };
return result; return result;
} }
fn lookupDeclaration(module: *Module, hashed: u32) !noreturn {
_ = hashed;
_ = module;
while (true) {}
}
fn getFile(module: *Module, allocator: Allocator, full_path: []const u8, relative_path: []const u8, package: *Package) !ImportFileResult { fn getFile(module: *Module, allocator: Allocator, full_path: []const u8, relative_path: []const u8, package: *Package) !ImportFileResult {
const path_lookup = try module.import_table.getOrPut(allocator, full_path); const path_lookup = try module.import_table.getOrPut(allocator, full_path);
const file: *File = switch (path_lookup.found_existing) { const file, const index = switch (path_lookup.found_existing) {
true => path_lookup.value_ptr.*, true => blk: {
const result = path_lookup.value_ptr.*;
const index = module.files.indexOf(result);
break :blk .{
result,
index,
};
},
false => blk: { false => blk: {
const new_file_index = try module.files.append(allocator, File{ const file_allocation = try module.files.append(allocator, File{
.relative_path = relative_path, .relative_path = relative_path,
.package = package, .package = package,
}); });
const file = module.files.get(new_file_index); std.debug.print("Adding file #{}: {s}\n", .{ file_allocation.index.uniqueInteger(), full_path });
path_lookup.value_ptr.* = file; path_lookup.value_ptr.* = file_allocation.ptr;
break :blk file; // break :blk file;
break :blk .{
file_allocation.ptr,
file_allocation.index,
};
}, },
}; };
return .{ return .{
.file = file, .ptr = file,
.index = index,
.is_new = !path_lookup.found_existing, .is_new = !path_lookup.found_existing,
}; };
} }
pub fn importPackage(module: *Module, allocator: Allocator, package: *Package) !ImportPackageResult { pub fn importPackage(module: *Module, allocator: Allocator, package: *Package) !ImportPackageResult {
const full_path = try std.fs.path.resolve(allocator, &.{ package.directory.path, package.source_path }); const full_path = try std.fs.path.resolve(allocator, &.{ package.directory.path, package.source_path });
const import = try module.getFile(allocator, full_path, package.source_path, package); const import_file = try module.getFile(allocator, full_path, package.source_path, package);
try import.file.addPackageReference(allocator, package); try import_file.ptr.addPackageReference(allocator, package);
return .{ return .{
.file = import.file, .file = import_file,
.is_new = import.is_new,
.is_package = true, .is_package = true,
}; };
} }
pub fn generateAbstractSyntaxTreeForFile(module: *Module, allocator: Allocator, file: *File) !void { pub fn generateAbstractSyntaxTreeForFile(module: *Module, allocator: Allocator, file: *File) !void {
_ = module; _ = module;
const source_file = try file.package.directory.handle.openFile(file.relative_path, .{}); const source_file = file.package.directory.handle.openFile(file.relative_path, .{}) catch |err| {
std.debug.panic("Can't find file {s} in directory {s} for error {s}", .{ file.relative_path, file.package.directory.path, @errorName(err) });
};
const file_size = try source_file.getEndPos(); const file_size = try source_file.getEndPos();
var file_buffer = try allocator.alloc(u8, file_size); var file_buffer = try allocator.alloc(u8, file_size);
@ -426,7 +486,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import); try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import);
} }
const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0]); const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0], .{ .block = 0, .index = 0 });
var ir = try intermediate_representation.initialize(compilation, module, packages[0], main_declaration); var ir = try intermediate_representation.initialize(compilation, module, packages[0], main_declaration);
@ -465,6 +525,9 @@ pub const File = struct {
relative_path: []const u8, relative_path: []const u8,
package: *Package, package: *Package,
pub const List = BlockList(@This());
pub const Index = List.Index;
const Status = enum { const Status = enum {
not_loaded, not_loaded,
loaded_into_memory, loaded_into_memory,
@ -484,15 +547,6 @@ pub const File = struct {
try file.file_references.append(allocator, affected); try file.file_references.append(allocator, affected);
} }
pub fn fromRelativePath(allocator: Allocator, file_relative_path: []const u8) *File {
const file_content = try std.fs.cwd().readFileAlloc(allocator, file_relative_path, std.math.maxInt(usize));
_ = file_content;
const file = try allocator.create(File);
file.* = File{};
return file;
}
fn lex(file: *File, allocator: Allocator) !void { fn lex(file: *File, allocator: Allocator) !void {
assert(file.status == .loaded_into_memory); assert(file.status == .loaded_into_memory);
file.lexical_analyzer_result = try lexical_analyzer.analyze(allocator, file.source_code); file.lexical_analyzer_result = try lexical_analyzer.analyze(allocator, file.source_code);

View File

@ -19,6 +19,7 @@ pub const Result = struct {
values: BlockList(Value) = .{}, values: BlockList(Value) = .{},
syscalls: BlockList(Syscall) = .{}, syscalls: BlockList(Syscall) = .{},
loads: BlockList(Load) = .{}, loads: BlockList(Load) = .{},
phis: BlockList(Phi) = .{},
}; };
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result { pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result {
@ -73,7 +74,10 @@ pub const Instruction = union(enum) {
}; };
const Phi = struct { const Phi = struct {
foo: u32 = 0, value: Value.Index,
jump: Jump.Index,
block: BasicBlock.Index,
next: Phi.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
@ -133,9 +137,10 @@ pub const Builder = struct {
module: *Module, module: *Module,
current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid, current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid,
current_function_index: Function.Index = Function.Index.invalid, current_function_index: Function.Index = Function.Index.invalid,
return_phi_node: Instruction.Index = Instruction.Index.invalid,
fn function(builder: *Builder, sema_function: Compilation.Function) !void { fn function(builder: *Builder, sema_function: Compilation.Function) !void {
builder.current_function_index = try builder.ir.functions.append(builder.allocator, .{}); builder.current_function_index = (try builder.ir.functions.append(builder.allocator, .{})).index;
// TODO: arguments // TODO: arguments
builder.current_basic_block = try builder.newBlock(); builder.current_basic_block = try builder.newBlock();
@ -143,15 +148,23 @@ pub const Builder = struct {
const is_noreturn = return_type.* == .noreturn; const is_noreturn = return_type.* == .noreturn;
if (!is_noreturn) { if (!is_noreturn) {
const exit_block = try builder.newBlock(); const exit_block = try builder.newBlock();
const phi = try builder.appendToBlock(exit_block, .{ const phi = try builder.ir.phis.addOne(builder.allocator);
.phi = Phi.Index.invalid, const phi_instruction = try builder.appendToBlock(exit_block, .{
.phi = phi.index,
}); });
phi.ptr.* = .{
.value = Value.Index.invalid,
.jump = Jump.Index.invalid,
.block = exit_block,
.next = Phi.Index.invalid,
};
const ret = try builder.appendToBlock(exit_block, .{ const ret = try builder.appendToBlock(exit_block, .{
.ret = .{ .ret = .{
.value = phi, .value = phi_instruction,
}, },
}); });
_ = ret; _ = ret;
builder.return_phi_node = phi_instruction;
} }
const sema_block = sema_function.getBodyBlock(builder.module); const sema_block = sema_function.getBodyBlock(builder.module);
try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn }); try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn });
@ -262,14 +275,35 @@ pub const Builder = struct {
} }
_ = try builder.append(.{ _ = try builder.append(.{
.syscall = try builder.ir.syscalls.append(builder.allocator, .{ .syscall = (try builder.ir.syscalls.append(builder.allocator, .{
.arguments = arguments, .arguments = arguments,
}), })).index,
}); });
}, },
.@"unreachable" => _ = try builder.append(.{ .@"unreachable" => _ = try builder.append(.{
.@"unreachable" = {}, .@"unreachable" = {},
}), }),
.@"return" => |sema_ret_index| {
const sema_ret = builder.module.returns.get(sema_ret_index);
const return_value = try builder.emitValue(sema_ret.value);
const phi_instruction = builder.ir.instructions.get(builder.return_phi_node);
const phi = builder.ir.phis.get(phi_instruction.phi);
const exit_jump = try builder.jump(.{ .source = builder.current_basic_block, .destination = phi.block });
phi_instruction.phi = (try builder.ir.phis.append(builder.allocator, .{
.value = return_value,
.jump = exit_jump,
.next = phi_instruction.phi,
.block = phi.block,
})).index;
_ = try builder.append(.{
.jump = exit_jump,
});
},
.declaration => |sema_declaration_index| {
_ = sema_declaration_index;
unreachable;
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
} }
@ -295,12 +329,12 @@ pub const Builder = struct {
const sema_value = builder.module.values.get(sema_value_index).*; const sema_value = builder.module.values.get(sema_value_index).*;
return switch (sema_value) { return switch (sema_value) {
// TODO // TODO
.integer => |integer| try builder.ir.values.append(builder.allocator, .{ .integer => |integer| (try builder.ir.values.append(builder.allocator, .{
.integer = .{ .integer = .{
.value = integer, .value = integer,
.sign = false, .sign = false,
}, },
}), })).index,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }
@ -308,7 +342,8 @@ pub const Builder = struct {
fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index { fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index {
const destination_block = builder.ir.blocks.get(jump_descriptor.destination); const destination_block = builder.ir.blocks.get(jump_descriptor.destination);
assert(!destination_block.sealed); assert(!destination_block.sealed);
return try builder.ir.jumps.append(builder.allocator, jump_descriptor); const jump_allocation = try builder.ir.jumps.append(builder.allocator, jump_descriptor);
return jump_allocation.index;
} }
fn append(builder: *Builder, instruction: Instruction) !Instruction.Index { fn append(builder: *Builder, instruction: Instruction) !Instruction.Index {
@ -317,20 +352,20 @@ pub const Builder = struct {
} }
fn appendToBlock(builder: *Builder, block_index: BasicBlock.Index, instruction: Instruction) !Instruction.Index { fn appendToBlock(builder: *Builder, block_index: BasicBlock.Index, instruction: Instruction) !Instruction.Index {
const instruction_index = try builder.ir.instructions.append(builder.allocator, instruction); const instruction_allocation = try builder.ir.instructions.append(builder.allocator, instruction);
try builder.ir.blocks.get(block_index).instructions.append(builder.allocator, instruction_index); try builder.ir.blocks.get(block_index).instructions.append(builder.allocator, instruction_allocation.index);
return instruction_index; return instruction_allocation.index;
} }
fn newBlock(builder: *Builder) !BasicBlock.Index { fn newBlock(builder: *Builder) !BasicBlock.Index {
const new_block_index = try builder.ir.blocks.append(builder.allocator, .{}); const new_block_allocation = try builder.ir.blocks.append(builder.allocator, .{});
const current_function = builder.ir.functions.get(builder.current_function_index); const current_function = builder.ir.functions.get(builder.current_function_index);
const function_block_index = current_function.blocks.items.len; const function_block_index = current_function.blocks.items.len;
try current_function.blocks.append(builder.allocator, new_block_index); try current_function.blocks.append(builder.allocator, new_block_allocation.index);
print("Adding block: {}\n", .{function_block_index}); print("Adding block: {}\n", .{function_block_index});
return new_block_index; return new_block_allocation.index;
} }
}; };

View File

@ -36,8 +36,8 @@ 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) {
block: u24,
index: u6, index: u6,
block: u24,
_reserved: bool = false, _reserved: bool = false,
valid: bool = true, valid: bool = true,
@ -50,6 +50,11 @@ pub fn BlockList(comptime T: type) type {
pub fn eq(index: Index, other: Index) bool { pub fn eq(index: Index, other: Index) bool {
return @as(u32, @bitCast(index)) == @as(u32, @bitCast(other)); return @as(u32, @bitCast(index)) == @as(u32, @bitCast(other));
} }
pub fn uniqueInteger(index: Index) u32 {
assert(index.valid);
return @as(u30, @truncate(@as(u32, @bitCast(index))));
}
}; };
pub const Iterator = struct { pub const Iterator = struct {
@ -81,6 +86,11 @@ pub fn BlockList(comptime T: type) type {
} }
}; };
pub const Allocation = struct {
ptr: *T,
index: Index,
};
pub fn iterator(list: *const List) Iterator { pub fn iterator(list: *const List) Iterator {
return .{ return .{
.block_index = 0, .block_index = 0,
@ -94,33 +104,50 @@ pub fn BlockList(comptime T: type) type {
return &list.blocks.items[index.block].items[index.index]; return &list.blocks.items[index.block].items[index.index];
} }
pub fn append(list: *List, allocator: Allocator, element: T) !Index { pub fn append(list: *List, allocator: Allocator, element: T) !Allocation {
const result = try list.addOne(allocator);
result.ptr.* = element;
return result;
}
pub fn addOne(list: *List, allocator: Allocator) !Allocation {
try list.ensureCapacity(allocator, list.len + 1); try list.ensureCapacity(allocator, list.len + 1);
const max_allocation = list.blocks.items.len * item_count; const max_allocation = list.blocks.items.len * item_count;
if (list.len < max_allocation) { const result = switch (list.len < max_allocation) {
// Follow the guess true => blk: {
if (list.blocks.items[list.first_block].allocateIndex()) |index| { const block = &list.blocks.items[list.first_block];
list.blocks.items[list.first_block].items[index] = element; if (block.allocateIndex()) |index| {
list.len += 1; const ptr = &block.items[index];
return .{ break :blk Allocation{
.index = index, .ptr = ptr,
.block = @intCast(list.first_block), .index = .{
.index = index,
.block = @intCast(list.first_block),
},
};
} else |_| {
@panic("TODO");
}
},
false => blk: {
const block_index = list.blocks.items.len;
const new_block = list.blocks.addOneAssumeCapacity();
new_block.* = .{};
const index = new_block.allocateIndex() catch unreachable;
const ptr = &new_block.items[index];
break :blk Allocation{
.ptr = ptr,
.index = .{
.index = index,
.block = @intCast(block_index),
},
}; };
} else |_| { },
@panic("TODO"); };
}
} else { list.len += 1;
const block_index = list.blocks.items.len;
const new_block = list.blocks.addOneAssumeCapacity(); return result;
new_block.* = .{};
const index = new_block.allocateIndex() catch unreachable;
new_block.items[index] = element;
list.len += 1;
return .{
.index = index,
.block = @intCast(block_index),
};
}
} }
pub fn ensureCapacity(list: *List, allocator: Allocator, new_capacity: usize) !void { pub fn ensureCapacity(list: *List, allocator: Allocator, new_capacity: usize) !void {
@ -131,6 +158,24 @@ pub fn BlockList(comptime T: type) type {
} }
} }
pub fn indexOf(list: *List, elem: *T) Index {
const address = @intFromPtr(elem);
std.debug.print("Items: {}. Block count: {}\n", .{ list.len, list.blocks.items.len });
for (list.blocks.items, 0..) |*block, block_index| {
const base = @intFromPtr(&block.items[0]);
const top = base + @sizeOf(T) * item_count;
std.debug.print("Bitset: {}. address: 0x{x}. Base: 0x{x}. Top: 0x{x}\n", .{ block.bitset, address, base, top });
if (address >= base and address < top) {
return .{
.block = @intCast(block_index),
.index = @intCast(@divExact(address - base, @sizeOf(T))),
};
}
}
@panic("not found");
}
test "Bitset index allocation" { test "Bitset index allocation" {
const expect = std.testing.expect; const expect = std.testing.expect;
var block = Block{}; var block = Block{};

View File

@ -34,6 +34,9 @@ pub const Token = packed struct(u64) {
fixed_keyword_false = 0x0d, fixed_keyword_false = 0x0d,
fixed_keyword_fn = 0x0e, fixed_keyword_fn = 0x0e,
fixed_keyword_unreachable = 0x0f, fixed_keyword_unreachable = 0x0f,
fixed_keyword_return = 0x10,
keyword_unsigned_integer = 0x1f,
keyword_signed_integer = 0x20,
bang = '!', // 0x21 bang = '!', // 0x21
hash = '#', // 0x23 hash = '#', // 0x23
dollar_sign = '$', // 0x24 dollar_sign = '$', // 0x24
@ -82,6 +85,7 @@ pub const FixedKeyword = enum {
false, false,
@"fn", @"fn",
@"unreachable", @"unreachable",
@"return",
}; };
pub const Result = struct { pub const Result = struct {
@ -109,8 +113,9 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result {
break; break;
} }
const identifier = text[start_index..][0 .. index - start_index]; // const identifier = text[start_index..][0 .. index - start_index];
std.debug.print("Identifier: {s}\n", .{identifier}); // _ = identifier;
// std.debug.print("Identifier: {s}\n", .{identifier});
if (start_character == 'u' or start_character == 's') { if (start_character == 'u' or start_character == 's') {
var index_integer = start_index + 1; var index_integer = start_index + 1;
@ -119,7 +124,13 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result {
} }
if (index_integer == index) { if (index_integer == index) {
unreachable; const id: Token.Id = switch (start_character) {
'u' => .keyword_unsigned_integer,
's' => .keyword_signed_integer,
else => unreachable,
};
break :blk id;
} }
} }
@ -127,7 +138,7 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result {
inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)), inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)),
} else .identifier; } else .identifier;
}, },
'(', ')', '{', '}', '-', '=', ';', '#', '@', ',' => |operator| blk: { '(', ')', '{', '}', '-', '=', ';', '#', '@', ',', '.' => |operator| blk: {
index += 1; index += 1;
break :blk @enumFromInt(operator); break :blk @enumFromInt(operator);
}, },

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ const Token = lexical_analyzer.Token;
pub const Result = struct { pub const Result = struct {
nodes: ArrayList(Node), nodes: ArrayList(Node),
node_lists: ArrayList(Node.List),
time: u64, time: u64,
}; };
@ -47,6 +48,11 @@ pub const Node = packed struct(u128) {
assert(index.valid); assert(index.valid);
return index.value; return index.value;
} }
pub fn uniqueInteger(index: Index) u32 {
assert(index.valid);
return index.value;
}
}; };
pub const Range = struct { pub const Range = struct {
@ -81,6 +87,15 @@ pub const Node = packed struct(u128) {
comptime_block_two = 23, comptime_block_two = 23,
block_two = 24, block_two = 24,
@"unreachable" = 25, @"unreachable" = 25,
field_access = 26,
call_one = 27,
comptime_block = 28,
block = 29,
unsigned_integer_type = 30,
signed_integer_type = 31,
main_one = 32,
main_two = 33,
main_zero = 34,
}; };
}; };
@ -109,10 +124,37 @@ const Analyzer = struct {
} }
} }
fn getIdentifier(analyzer: *const Analyzer, token: Token) []const u8 { fn bytes(analyzer: *const Analyzer, token_index: Token.Index) []const u8 {
assert(token.id == .identifier); const token = analyzer.tokens[token_index];
const identifier = analyzer.file[token.start..][0..token.len]; return analyzer.file[token.start..][0..token.len];
return identifier; }
fn symbolDeclaration(analyzer: *Analyzer) !Node.Index {
const first = analyzer.token_i;
assert(analyzer.tokens[first].id == .fixed_keyword_var or analyzer.tokens[first].id == .fixed_keyword_const);
analyzer.token_i += 1;
_ = try analyzer.expectToken(.identifier);
// TODO: type
_ = try analyzer.expectToken(.equal);
const init_node = try analyzer.expression();
_ = try analyzer.expectToken(.semicolon);
// TODO:
const type_node = Node.Index.invalid;
const declaration = Node{
.id = .simple_variable_declaration,
.token = first,
.left = type_node,
.right = init_node,
};
const declaration_init_node = analyzer.nodes.items[init_node.unwrap()];
std.debug.print("Declaration init node: {}\n", .{declaration_init_node});
return analyzer.addNode(declaration);
} }
fn containerMembers(analyzer: *Analyzer) !Members { fn containerMembers(analyzer: *Analyzer) !Members {
@ -121,58 +163,26 @@ const Analyzer = struct {
while (analyzer.token_i < analyzer.tokens.len) { while (analyzer.token_i < analyzer.tokens.len) {
const first = analyzer.token_i; const first = analyzer.token_i;
const member_node: Node = switch (analyzer.tokens[first].id) { const member_node_index: Node.Index = switch (analyzer.tokens[first].id) {
.fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) { .fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) {
.left_brace => blk: { .left_brace => blk: {
analyzer.token_i += 1; analyzer.token_i += 1;
const comptime_block = try analyzer.block(.{ .is_comptime = true }); const comptime_block = try analyzer.block(.{ .is_comptime = true });
break :blk .{ break :blk try analyzer.addNode(.{
.id = .@"comptime", .id = .@"comptime",
.token = first, .token = first,
.left = comptime_block, .left = comptime_block,
.right = Node.Index.invalid, .right = Node.Index.invalid,
}; });
}, },
else => |foo| std.debug.panic("NI: {s}", .{@tagName(foo)}), else => |foo| @panic(@tagName(foo)),
}, },
.fixed_keyword_const, .fixed_keyword_var => blk: { .fixed_keyword_const, .fixed_keyword_var => try analyzer.symbolDeclaration(),
analyzer.token_i += 1; else => |t| @panic(@tagName(t)),
_ = try analyzer.expectToken(.identifier);
// TODO: type
_ = try analyzer.expectToken(.equal);
// TODO: do this in a function
const init_node = try analyzer.expression();
// const init_node = switch (analyzer.tokens[analyzer.token_i].id) {
// .identifier => unreachable,
// .hash => try analyzer.compilerIntrinsic(),
// .left_parenthesis => try analyzer.function(),
// else => |t| std.debug.panic("NI: {s}", .{@tagName(t)}),
// };
_ = try analyzer.expectToken(.semicolon);
// TODO:
const type_node = Node.Index.invalid;
const top_level_decl = .{
.id = .simple_variable_declaration,
.token = first,
.left = type_node,
.right = init_node,
};
break :blk top_level_decl;
},
.identifier => {
unreachable;
},
else => |t| std.debug.panic("NI: {s}", .{@tagName(t)}),
}; };
const member_index = try analyzer.addNode(member_node); try analyzer.temporal_node_heap.append(analyzer.allocator, member_node_index);
try analyzer.temporal_node_heap.append(analyzer.allocator, member_index);
} }
const members_array = analyzer.temporal_node_heap.items[node_heap_top..]; const members_array = analyzer.temporal_node_heap.items[node_heap_top..];
@ -263,10 +273,12 @@ const Analyzer = struct {
}, },
else => try analyzer.assignExpressionStatement(), else => try analyzer.assignExpressionStatement(),
}, },
.fixed_keyword_unreachable => try analyzer.assignExpressionStatement(), .fixed_keyword_unreachable, .fixed_keyword_return => try analyzer.assignExpressionStatement(),
.fixed_keyword_while => try analyzer.whileStatement(options), .fixed_keyword_while => try analyzer.whileStatement(options),
else => unreachable, .fixed_keyword_const, .fixed_keyword_var => try analyzer.symbolDeclaration(),
else => |t| @panic(@tagName(t)),
}; };
try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index); try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index);
} }
@ -301,7 +313,15 @@ const Analyzer = struct {
.left = statement_array[0], .left = statement_array[0],
.right = statement_array[1], .right = statement_array[1],
}, },
else => |len| std.debug.panic("len: {}", .{len}), else => .{
.id = switch (options.is_comptime) {
true => .comptime_block,
false => .block,
},
.token = left_brace,
.left = try analyzer.nodeList(statement_array),
.right = Node.Index.invalid,
},
}; };
return analyzer.addNode(node); return analyzer.addNode(node);
} }
@ -329,7 +349,7 @@ const Analyzer = struct {
const expression_id: Node.Id = switch (analyzer.tokens[analyzer.token_i].id) { const expression_id: Node.Id = switch (analyzer.tokens[analyzer.token_i].id) {
.semicolon => return expr, .semicolon => return expr,
.equal => .assign, .equal => .assign,
else => unreachable, else => |t| @panic(@tagName(t)),
}; };
const node = Node{ const node = Node{
@ -398,8 +418,8 @@ const Analyzer = struct {
while (analyzer.token_i < analyzer.tokens.len) { while (analyzer.token_i < analyzer.tokens.len) {
const precedence: i32 = switch (analyzer.tokens[analyzer.token_i].id) { const precedence: i32 = switch (analyzer.tokens[analyzer.token_i].id) {
.equal, .semicolon, .right_parenthesis, .right_brace, .comma => -1, .equal, .semicolon, .right_parenthesis, .right_brace, .comma, .period => -1,
else => |foo| std.debug.panic("Foo: ({s}) {}", .{ @tagName(foo), foo }), else => |t| @panic(@tagName(t)),
}; };
if (precedence < minimum_precedence) { if (precedence < minimum_precedence) {
@ -446,6 +466,16 @@ const Analyzer = struct {
}, },
.string_literal, .number_literal, .fixed_keyword_true, .fixed_keyword_false, .hash, .fixed_keyword_unreachable => try analyzer.curlySuffixExpression(), .string_literal, .number_literal, .fixed_keyword_true, .fixed_keyword_false, .hash, .fixed_keyword_unreachable => try analyzer.curlySuffixExpression(),
.fixed_keyword_fn => analyzer.function(), .fixed_keyword_fn => analyzer.function(),
.fixed_keyword_return => try analyzer.addNode(.{
.id = .@"return",
.token = blk: {
const token = analyzer.token_i;
analyzer.token_i += 1;
break :blk token;
},
.left = try analyzer.expression(),
.right = Node.Index.invalid,
}),
// todo:? // todo:?
// .left_brace => try analyzer.block(), // .left_brace => try analyzer.block(),
else => |id| { else => |id| {
@ -492,14 +522,8 @@ const Analyzer = struct {
fn typeExpression(analyzer: *Analyzer) !Node.Index { fn typeExpression(analyzer: *Analyzer) !Node.Index {
return switch (analyzer.tokens[analyzer.token_i].id) { return switch (analyzer.tokens[analyzer.token_i].id) {
.identifier, .fixed_keyword_noreturn, .fixed_keyword_true, .fixed_keyword_false, .hash => try analyzer.errorUnionExpression(), .identifier, .fixed_keyword_noreturn, .fixed_keyword_true, .fixed_keyword_false, .hash, .string_literal, .number_literal, .fixed_keyword_unreachable, .keyword_unsigned_integer, .keyword_signed_integer => try analyzer.errorUnionExpression(),
else => |id| blk: { else => |id| @panic(@tagName(id)),
log.warn("By default, calling errorUnionExpression with {s}", .{@tagName(id)});
const result = try analyzer.errorUnionExpression();
break :blk result;
},
}; };
} }
@ -516,14 +540,17 @@ const Analyzer = struct {
var result = try analyzer.primaryTypeExpression(); var result = try analyzer.primaryTypeExpression();
while (true) { while (true) {
if (analyzer.suffixOperator()) |_| { const suffix_operator = try analyzer.suffixOperator(result);
unreachable; if (suffix_operator.valid) {
result = suffix_operator;
} else { } else {
if (analyzer.tokens[analyzer.token_i].id == .left_parenthesis) { if (analyzer.tokens[analyzer.token_i].id == .left_parenthesis) {
const left_parenthesis = analyzer.token_i;
analyzer.token_i += 1; analyzer.token_i += 1;
var expression_list = ArrayList(Node.Index){}; var expression_list = ArrayList(Node.Index){};
while (analyzer.tokens[analyzer.token_i].id != .right_parenthesis) { while (analyzer.tokens[analyzer.token_i].id != .right_parenthesis) {
std.debug.print("Loop\n", .{});
const parameter = try analyzer.expression(); const parameter = try analyzer.expression();
try expression_list.append(analyzer.allocator, parameter); try expression_list.append(analyzer.allocator, parameter);
analyzer.token_i += @intFromBool(switch (analyzer.tokens[analyzer.token_i].id) { analyzer.token_i += @intFromBool(switch (analyzer.tokens[analyzer.token_i].id) {
@ -534,7 +561,16 @@ const Analyzer = struct {
} }
_ = try analyzer.expectToken(.right_parenthesis); _ = try analyzer.expectToken(.right_parenthesis);
@panic("TODO"); // const is_comma = analyzer.tokens[analyzer.token_i].id == .comma;
return analyzer.addNode(switch (expression_list.items.len) {
0 => .{
.id = .call_one,
.token = left_parenthesis,
.left = result,
.right = Node.Index.invalid,
},
else => |len| std.debug.panic("len: {}", .{len}),
});
} else { } else {
return result; return result;
} }
@ -569,8 +605,8 @@ const Analyzer = struct {
.identifier => switch (analyzer.tokens[token_i + 1].id) { .identifier => switch (analyzer.tokens[token_i + 1].id) {
.colon => unreachable, .colon => unreachable,
else => blk: { else => blk: {
const identifier = analyzer.getIdentifier(token); const identifier = analyzer.bytes(token_i);
std.debug.print("identifier: {s}\n", .{identifier}); // std.debug.print("identifier: {s}\n", .{identifier});
analyzer.token_i += 1; analyzer.token_i += 1;
if (equal(u8, identifier, "_")) { if (equal(u8, identifier, "_")) {
break :blk Node.Index.invalid; break :blk Node.Index.invalid;
@ -594,20 +630,55 @@ const Analyzer = struct {
.right = Node.Index.invalid, .right = Node.Index.invalid,
}), }),
.hash => analyzer.compilerIntrinsic(), .hash => analyzer.compilerIntrinsic(),
.keyword_unsigned_integer, .keyword_signed_integer => |signedness| try analyzer.addNode(.{
.id = switch (signedness) {
.keyword_unsigned_integer => .unsigned_integer_type,
.keyword_signed_integer => .signed_integer_type,
else => unreachable,
},
.token = blk: {
analyzer.token_i += 1;
break :blk token_i;
},
.left = @bitCast(@as(u32, std.fmt.parseInt(u16, analyzer.bytes(token_i)[1..], 10) catch unreachable)),
.right = Node.Index.invalid,
}),
else => |foo| { else => |foo| {
switch (foo) { switch (foo) {
.identifier => std.debug.panic("{s}: {s}", .{ @tagName(foo), analyzer.getIdentifier(analyzer.tokens[token_i]) }), .identifier => std.debug.panic("{s}: {s}", .{ @tagName(foo), analyzer.bytes(token_i) }),
else => std.debug.panic("{s}", .{@tagName(foo)}), else => @panic(@tagName(foo)),
} }
}, },
}; };
} }
// TODO: // TODO:
fn suffixOperator(analyzer: *Analyzer) ?bool { fn suffixOperator(analyzer: *Analyzer, left: Node.Index) !Node.Index {
_ = analyzer; const token = analyzer.tokens[analyzer.token_i];
return switch (token.id) {
return null; .left_bracket => unreachable,
.period => switch (analyzer.tokens[analyzer.token_i + 1].id) {
.identifier => analyzer.addNode(.{
.id = .field_access,
.token = blk: {
const main_token = analyzer.token_i;
analyzer.token_i += 1;
break :blk main_token;
},
.left = left,
.right = blk: {
//TODO ???
const right_token = analyzer.token_i;
analyzer.token_i += 1;
const result: Node.Index = @bitCast(right_token);
std.debug.print("WARNING: rhs has node index {} but it's token #{}\n", .{ result, right_token });
break :blk result;
},
}),
else => |t| @panic(@tagName(t)),
},
else => Node.Index.invalid,
};
} }
fn addNode(analyzer: *Analyzer, node: Node) !Node.Index { fn addNode(analyzer: *Analyzer, node: Node) !Node.Index {
@ -618,27 +689,23 @@ const Analyzer = struct {
.value = @intCast(index), .value = @intCast(index),
}; };
} }
fn nodeList(analyzer: *Analyzer, input: []const Node.Index) !Node.Index {
const index = analyzer.node_lists.items.len;
var new_node_list = try ArrayList(Node.Index).initCapacity(analyzer.allocator, input.len);
try new_node_list.appendSlice(analyzer.allocator, input);
try analyzer.node_lists.append(analyzer.allocator, new_node_list);
return .{
.value = @intCast(index),
};
}
}; };
const Members = struct { const Members = struct {
len: usize, len: usize,
left: Node.Index, left: Node.Index,
right: Node.Index, right: Node.Index,
pub fn toRange(members: Members) Node.Range {
return switch (members.len) {
0 => unreachable,
1 => .{
.start = members.left.value,
.end = members.left.value,
},
2 => .{
.start = members.left.value,
.end = members.right.value,
},
else => unreachable,
};
}
}; };
pub fn analyze(allocator: Allocator, tokens: []const Token, file: []const u8) !Result { pub fn analyze(allocator: Allocator, tokens: []const Token, file: []const u8) !Result {
@ -657,10 +724,22 @@ pub fn analyze(allocator: Allocator, tokens: []const Token, file: []const u8) !R
assert(node_index.value == 0); assert(node_index.value == 0);
assert(node_index.valid); assert(node_index.valid);
const members = try analyzer.containerMembers(); const members = try analyzer.containerMembers();
const member_range = members.toRange();
analyzer.nodes.items[0].left = .{ .value = @intCast(member_range.start) }; switch (members.len) {
analyzer.nodes.items[0].right = .{ .value = @intCast(member_range.end) }; 0 => unreachable,
1 => {
analyzer.nodes.items[0].id = .main_one;
analyzer.nodes.items[0].left = members.left;
},
2 => {
analyzer.nodes.items[0].id = .main_two;
analyzer.nodes.items[0].left = members.left;
analyzer.nodes.items[0].right = members.right;
},
else => unreachable,
}
const end = std.time.Instant.now() catch unreachable; const end = std.time.Instant.now() catch unreachable;
@ -668,6 +747,7 @@ pub fn analyze(allocator: Allocator, tokens: []const Token, file: []const u8) !R
return .{ return .{
.nodes = analyzer.nodes, .nodes = analyzer.nodes,
.node_lists = analyzer.node_lists,
.time = end.since(start), .time = end.since(start),
}; };
} }

View File

@ -1,8 +1,6 @@
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
pub const first = "src/test/main.b";
pub fn readFile(allocator: Allocator, file_relative_path: []const u8) ![]const u8 { pub fn readFile(allocator: Allocator, file_relative_path: []const u8) ![]const u8 {
const file = try std.fs.cwd().readFileAlloc(allocator, file_relative_path, std.math.maxInt(usize)); const file = try std.fs.cwd().readFileAlloc(allocator, file_relative_path, std.math.maxInt(usize));
return file; return file;

View File

@ -5,7 +5,7 @@ const assert = std.debug.assert;
const Compilation = @import("Compilation.zig"); const Compilation = @import("Compilation.zig");
pub const seed = std.math.maxInt(u64); pub const seed = std.math.maxInt(u64);
const default_src_file = "src/test/main.b"; const default_src_file = "src/test/main.nat";
pub fn main() !void { pub fn main() !void {
try singleCompilation(default_src_file); try singleCompilation(default_src_file);

View File

@ -1,3 +1,3 @@
const main = fn() i32 { const main = fn() s32 {
return 0; return 0;
}; };