diff --git a/lib/std/start.nat b/lib/std/start.nat index 81360d0..031890b 100644 --- a/lib/std/start.nat +++ b/lib/std/start.nat @@ -3,6 +3,7 @@ comptime { } const _start = fn () noreturn { - _ = #syscall(231, 0); + const result = #import("main").main(); + _ = #syscall(231, result); unreachable; }; diff --git a/src/Compilation.zig b/src/Compilation.zig index a844add..6a7e357 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -64,6 +64,7 @@ pub const Struct = struct { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Type = union(enum) { @@ -74,6 +75,7 @@ pub const Type = union(enum) { @"struct": Struct.Index, pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Integer = struct { @@ -87,12 +89,14 @@ pub const Integer = struct { /// A scope contains a bunch of declarations pub const Scope = struct { - parent: Scope.Index, - type: Type.Index = Type.Index.invalid, declarations: AutoHashMap(u32, Declaration.Index) = .{}, + parent: Scope.Index, + file: File.Index, + type: Type.Index = Type.Index.invalid, pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const ScopeType = enum(u1) { @@ -113,6 +117,7 @@ pub const Declaration = struct { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Function = struct { @@ -133,6 +138,7 @@ pub const Function = struct { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Block = struct { @@ -140,6 +146,7 @@ pub const Block = struct { reaches_end: bool, pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Field = struct { @@ -147,6 +154,7 @@ pub const Field = struct { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Loop = struct { @@ -156,6 +164,7 @@ pub const Loop = struct { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; const Runtime = struct { @@ -172,6 +181,7 @@ pub const Assignment = struct { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; }; pub const Syscall = struct { @@ -185,11 +195,35 @@ pub const Syscall = struct { pub const List = BlockList(@This()); 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) { unresolved: Unresolved, declaration: Declaration.Index, + declaration_reference: Declaration.Index, void, bool: bool, undefined, @@ -202,9 +236,13 @@ pub const Value = union(enum) { type: Type.Index, integer: u64, syscall: Syscall.Index, + call: Call.Index, + argument_list: ArgumentList, + @"return": Return.Index, pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Allocation = List.Allocation; pub fn isComptime(value: Value) bool { return switch (value) { @@ -238,23 +276,26 @@ pub const Module = struct { loops: BlockList(Loop) = .{}, assignments: BlockList(Assignment) = .{}, syscalls: BlockList(Syscall) = .{}, + calls: BlockList(Call) = .{}, + argument_list: BlockList(ArgumentList) = .{}, + returns: BlockList(Return) = .{}, pub const Descriptor = struct { main_package_path: []const u8, }; const ImportFileResult = struct { - file: *File, + ptr: *File, + index: File.Index, is_new: bool, }; const ImportPackageResult = struct { - file: *File, - is_new: bool, + file: ImportFileResult, 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}); if (equal(u8, import_name, "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); } + const current_file = module.files.get(current_file_index); if (current_file.package.dependencies.get(import_name)) |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 file_relative_path = std.fs.path.basename(full_path); 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{ - .file = import.file, - .is_new = import.is_new, + .file = import_file, .is_package = false, }; 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 { const path_lookup = try module.import_table.getOrPut(allocator, full_path); - const file: *File = switch (path_lookup.found_existing) { - true => path_lookup.value_ptr.*, + const file, const index = switch (path_lookup.found_existing) { + true => blk: { + const result = path_lookup.value_ptr.*; + const index = module.files.indexOf(result); + break :blk .{ + result, + index, + }; + }, 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, .package = package, }); - const file = module.files.get(new_file_index); - path_lookup.value_ptr.* = file; - break :blk file; + std.debug.print("Adding file #{}: {s}\n", .{ file_allocation.index.uniqueInteger(), full_path }); + path_lookup.value_ptr.* = file_allocation.ptr; + // break :blk file; + break :blk .{ + file_allocation.ptr, + file_allocation.index, + }; }, }; return .{ - .file = file, + .ptr = file, + .index = index, .is_new = !path_lookup.found_existing, }; } 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 import = try module.getFile(allocator, full_path, package.source_path, package); - try import.file.addPackageReference(allocator, package); + const import_file = try module.getFile(allocator, full_path, package.source_path, package); + try import_file.ptr.addPackageReference(allocator, package); return .{ - .file = import.file, - .is_new = import.is_new, + .file = import_file, .is_package = true, }; } pub fn generateAbstractSyntaxTreeForFile(module: *Module, allocator: Allocator, file: *File) !void { _ = 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(); 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); } - 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); @@ -465,6 +525,9 @@ pub const File = struct { relative_path: []const u8, package: *Package, + pub const List = BlockList(@This()); + pub const Index = List.Index; + const Status = enum { not_loaded, loaded_into_memory, @@ -484,15 +547,6 @@ pub const File = struct { 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 { assert(file.status == .loaded_into_memory); file.lexical_analyzer_result = try lexical_analyzer.analyze(allocator, file.source_code); diff --git a/src/backend/intermediate_representation.zig b/src/backend/intermediate_representation.zig index f96baca..e817d4a 100644 --- a/src/backend/intermediate_representation.zig +++ b/src/backend/intermediate_representation.zig @@ -19,6 +19,7 @@ pub const Result = struct { values: BlockList(Value) = .{}, syscalls: BlockList(Syscall) = .{}, loads: BlockList(Load) = .{}, + phis: BlockList(Phi) = .{}, }; 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 { - foo: u32 = 0, + value: Value.Index, + jump: Jump.Index, + block: BasicBlock.Index, + next: Phi.Index, pub const List = BlockList(@This()); pub const Index = List.Index; }; @@ -133,9 +137,10 @@ pub const Builder = struct { module: *Module, current_basic_block: BasicBlock.Index = BasicBlock.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 { - 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 builder.current_basic_block = try builder.newBlock(); @@ -143,15 +148,23 @@ pub const Builder = struct { const is_noreturn = return_type.* == .noreturn; if (!is_noreturn) { const exit_block = try builder.newBlock(); - const phi = try builder.appendToBlock(exit_block, .{ - .phi = Phi.Index.invalid, + const phi = try builder.ir.phis.addOne(builder.allocator); + 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, .{ .ret = .{ - .value = phi, + .value = phi_instruction, }, }); _ = ret; + builder.return_phi_node = phi_instruction; } const sema_block = sema_function.getBodyBlock(builder.module); try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn }); @@ -262,14 +275,35 @@ pub const Builder = struct { } _ = try builder.append(.{ - .syscall = try builder.ir.syscalls.append(builder.allocator, .{ + .syscall = (try builder.ir.syscalls.append(builder.allocator, .{ .arguments = arguments, - }), + })).index, }); }, .@"unreachable" => _ = try builder.append(.{ .@"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)), } } @@ -295,12 +329,12 @@ pub const Builder = struct { const sema_value = builder.module.values.get(sema_value_index).*; return switch (sema_value) { // TODO - .integer => |integer| try builder.ir.values.append(builder.allocator, .{ + .integer => |integer| (try builder.ir.values.append(builder.allocator, .{ .integer = .{ .value = integer, .sign = false, }, - }), + })).index, else => |t| @panic(@tagName(t)), }; } @@ -308,7 +342,8 @@ pub const Builder = struct { fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index { const destination_block = builder.ir.blocks.get(jump_descriptor.destination); 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 { @@ -317,20 +352,20 @@ pub const Builder = struct { } fn appendToBlock(builder: *Builder, block_index: BasicBlock.Index, instruction: Instruction) !Instruction.Index { - const instruction_index = try builder.ir.instructions.append(builder.allocator, instruction); - try builder.ir.blocks.get(block_index).instructions.append(builder.allocator, instruction_index); + const instruction_allocation = try builder.ir.instructions.append(builder.allocator, instruction); + 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 { - 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 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}); - return new_block_index; + return new_block_allocation.index; } }; diff --git a/src/data_structures.zig b/src/data_structures.zig index cc47ff3..7afff5d 100644 --- a/src/data_structures.zig +++ b/src/data_structures.zig @@ -36,8 +36,8 @@ pub fn BlockList(comptime T: type) type { const List = @This(); pub const Index = packed struct(u32) { - block: u24, index: u6, + block: u24, _reserved: bool = false, valid: bool = true, @@ -50,6 +50,11 @@ pub fn BlockList(comptime T: type) type { pub fn eq(index: Index, other: Index) bool { 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 { @@ -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 { return .{ .block_index = 0, @@ -94,33 +104,50 @@ pub fn BlockList(comptime T: type) type { 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); const max_allocation = list.blocks.items.len * item_count; - if (list.len < max_allocation) { - // Follow the guess - if (list.blocks.items[list.first_block].allocateIndex()) |index| { - list.blocks.items[list.first_block].items[index] = element; - list.len += 1; - return .{ - .index = index, - .block = @intCast(list.first_block), + const result = switch (list.len < max_allocation) { + true => blk: { + const block = &list.blocks.items[list.first_block]; + if (block.allocateIndex()) |index| { + const ptr = &block.items[index]; + break :blk Allocation{ + .ptr = ptr, + .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 { - const block_index = list.blocks.items.len; - const new_block = list.blocks.addOneAssumeCapacity(); - new_block.* = .{}; - const index = new_block.allocateIndex() catch unreachable; - new_block.items[index] = element; - list.len += 1; - return .{ - .index = index, - .block = @intCast(block_index), - }; - } + }, + }; + + list.len += 1; + + return result; } 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" { const expect = std.testing.expect; var block = Block{}; diff --git a/src/frontend/lexical_analyzer.zig b/src/frontend/lexical_analyzer.zig index c5745fc..fc63727 100644 --- a/src/frontend/lexical_analyzer.zig +++ b/src/frontend/lexical_analyzer.zig @@ -34,6 +34,9 @@ pub const Token = packed struct(u64) { fixed_keyword_false = 0x0d, fixed_keyword_fn = 0x0e, fixed_keyword_unreachable = 0x0f, + fixed_keyword_return = 0x10, + keyword_unsigned_integer = 0x1f, + keyword_signed_integer = 0x20, bang = '!', // 0x21 hash = '#', // 0x23 dollar_sign = '$', // 0x24 @@ -82,6 +85,7 @@ pub const FixedKeyword = enum { false, @"fn", @"unreachable", + @"return", }; pub const Result = struct { @@ -109,8 +113,9 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result { break; } - const identifier = text[start_index..][0 .. index - start_index]; - std.debug.print("Identifier: {s}\n", .{identifier}); + // const identifier = text[start_index..][0 .. index - start_index]; + // _ = identifier; + // std.debug.print("Identifier: {s}\n", .{identifier}); if (start_character == 'u' or start_character == 's') { var index_integer = start_index + 1; @@ -119,7 +124,13 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result { } 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)), } else .identifier; }, - '(', ')', '{', '}', '-', '=', ';', '#', '@', ',' => |operator| blk: { + '(', ')', '{', '}', '-', '=', ';', '#', '@', ',', '.' => |operator| blk: { index += 1; break :blk @enumFromInt(operator); }, diff --git a/src/frontend/semantic_analyzer.zig b/src/frontend/semantic_analyzer.zig index a01deac..438cd95 100644 --- a/src/frontend/semantic_analyzer.zig +++ b/src/frontend/semantic_analyzer.zig @@ -7,6 +7,7 @@ const File = Compilation.File; const Module = Compilation.Module; const Package = Compilation.Package; +const ArgumentList = Compilation.ArgumentList; const Assignment = Compilation.Assignment; const Block = Compilation.Block; const Declaration = Compilation.Declaration; @@ -14,6 +15,7 @@ const Field = Compilation.Field; const Function = Compilation.Function; const Loop = Compilation.Loop; const Scope = Compilation.Scope; +const ScopeType = Compilation.ScopeType; const Struct = Compilation.Struct; const Type = Compilation.Type; const Value = Compilation.Value; @@ -33,35 +35,68 @@ const HashMap = data_structures.AutoHashMap; const print = std.debug.print; const Analyzer = struct { - source_code: []const u8, - nodes: []const Node, - tokens: []const Token, - file: *File, allocator: Allocator, module: *Module, + current_file: File.Index, - fn lazyGlobalDeclaration(analyzer: *Analyzer, node_index: Node.Index) void { - print("Global: {}", .{analyzer.nodes[node_index.unwrap()]}); + fn getSourceFile(analyzer: *Analyzer, scope_index: Scope.Index) []const u8 { + const scope = analyzer.module.scopes.get(scope_index); + const file = analyzer.module.files.get(scope.file); + return file.source_code; } - fn comptimeBlock(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index { - const comptime_node = analyzer.nodes[node_index.unwrap()]; + fn getNode(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) Node { + const scope = analyzer.module.scopes.get(scope_index); + const file = analyzer.module.files.get(scope.file); + const result = file.syntactic_analyzer_result.nodes.items[node_index.unwrap()]; + return result; + } - const comptime_block = try analyzer.block(scope, .{ .none = {} }, comptime_node.left); - return try analyzer.module.values.append(analyzer.allocator, .{ + fn getToken(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index) Token { + const scope = analyzer.module.scopes.get(scope_index); + const file = analyzer.module.files.get(scope.file); + const result = file.lexical_analyzer_result.tokens.items[token_index]; + + return result; + } + + fn getNodeList(analyzer: *Analyzer, scope_index: Scope.Index, list_index: u32) ArrayList(Node.Index) { + const scope = analyzer.module.scopes.get(scope_index); + const file = analyzer.module.files.get(scope.file); + return file.syntactic_analyzer_result.node_lists.items[list_index]; + } + + fn comptimeBlock(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Value.Index { + const comptime_node = analyzer.getNode(scope_index, node_index); + + const comptime_block = try analyzer.block(scope_index, .{ .none = {} }, comptime_node.left); + const value_allocation = try analyzer.module.values.append(analyzer.allocator, .{ .block = comptime_block, }); + return value_allocation.index; } - fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Assignment.Index { - _ = node_index; - _ = scope; - _ = analyzer; + fn unresolved(analyzer: *Analyzer, node_index: Node.Index) !Value.Allocation { + const value_allocation = try analyzer.module.values.addOne(analyzer.allocator); + value_allocation.ptr.* = .{ + .unresolved = .{ + .node_index = node_index, + }, + }; + + return value_allocation; } - fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index { + fn unresolvedAllocate(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value.Allocation { + const new = try analyzer.unresolved(node_index); + try analyzer.resolveNode(new.ptr, scope_index, expect_type, node_index); + return new; + } + + fn block(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index { + _ = expect_type; var reaches_end = true; - const block_node = analyzer.nodes[node_index.unwrap()]; + const block_node = analyzer.getNode(scope_index, node_index); var statement_nodes = ArrayList(Node.Index){}; switch (block_node.id) { .block_one, .comptime_block_one => { @@ -72,12 +107,13 @@ const Analyzer = struct { try statement_nodes.append(analyzer.allocator, block_node.left); try statement_nodes.append(analyzer.allocator, block_node.right); }, + .block, .comptime_block => statement_nodes = analyzer.getNodeList(scope_index, block_node.left.unwrap()), else => |t| @panic(@tagName(t)), } const is_comptime = switch (block_node.id) { - .comptime_block_zero, .comptime_block_one, .comptime_block_two => true, - .block_zero, .block_one, .block_two => false, + .comptime_block, .comptime_block_zero, .comptime_block_one, .comptime_block_two => true, + .block, .block_zero, .block_one, .block_two => false, else => |t| @panic(@tagName(t)), }; print("Is comptime: {}\n", .{is_comptime}); @@ -89,7 +125,7 @@ const Analyzer = struct { unreachable; } - const statement_node = analyzer.nodes[statement_node_index.unwrap()]; + const statement_node = analyzer.getNode(scope_index, statement_node_index); const statement_value = switch (statement_node.id) { inline .assign, .simple_while => |statement_id| blk: { const specific_value_index = switch (statement_id) { @@ -99,17 +135,23 @@ const Analyzer = struct { switch (statement_node.left.valid) { // In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result``` false => { - const right = try analyzer.expression(scope, ExpectType.none, statement_node.right); - try statements.append(analyzer.allocator, right); + const right_value_allocation = try analyzer.module.values.addOne(analyzer.allocator); + right_value_allocation.ptr.* = .{ + .unresolved = .{ + .node_index = statement_node.right, + }, + }; + try analyzer.resolveNode(right_value_allocation.ptr, scope_index, ExpectType.none, statement_node.right); + switch (right_value_allocation.ptr.*) { + else => |t| std.debug.print("\n\n\n\n\nASSIGN RIGHT: {s}\n\n\n\n", .{@tagName(t)}), + } + try statements.append(analyzer.allocator, right_value_allocation.index); continue; }, true => { - const left_node = analyzer.nodes[statement_node.left.unwrap()]; - print("left node index: {}. Left node: {}\n", .{ statement_node.left, left_node }); // const id = analyzer.tokenIdentifier(.token); // print("id: {s}\n", .{id}); - const left = try analyzer.expression(scope, ExpectType.none, statement_node.left); - _ = left; + // const left = try analyzer.expression(scope_index, ExpectType.none, statement_node.left); // if (analyzer.module.values.get(left).isComptime() and analyzer.module.values.get(right).isComptime()) { // unreachable; @@ -125,20 +167,18 @@ const Analyzer = struct { } }, .simple_while => statement: { - const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{ + const loop_allocation = try analyzer.module.loops.append(analyzer.allocator, .{ .condition = Value.Index.invalid, .body = Value.Index.invalid, .breaks = false, }); - const loop_structure = analyzer.module.loops.get(loop_index); - const while_condition = try analyzer.expression(scope, ExpectType.boolean, statement_node.left); - const while_body = try analyzer.expression(scope, expect_type, statement_node.right); - loop_structure.condition = while_condition; - loop_structure.body = while_body; + loop_allocation.ptr.condition = (try analyzer.unresolvedAllocate(scope_index, ExpectType.boolean, statement_node.left)).index; + loop_allocation.ptr.body = (try analyzer.unresolvedAllocate(scope_index, ExpectType.none, statement_node.right)).index; - reaches_end = loop_structure.breaks or while_condition.valid; + // TODO: bool true + reaches_end = loop_allocation.ptr.breaks or unreachable; - break :statement loop_index; + break :statement loop_allocation.index; }, else => unreachable, }; @@ -147,62 +187,87 @@ const Analyzer = struct { .simple_while => "loop", else => unreachable, }, specific_value_index); - const value_index = try analyzer.module.values.append(analyzer.allocator, value); - break :blk value_index; + const value_allocation = try analyzer.module.values.append(analyzer.allocator, value); + break :blk value_allocation.index; }, .@"unreachable" => blk: { reaches_end = false; break :blk Values.@"unreachable".getIndex(); }, + .simple_variable_declaration => (try analyzer.module.values.append(analyzer.allocator, .{ + .declaration = try analyzer.symbolDeclaration(scope_index, statement_node_index, .local), + })).index, + .@"return" => blk: { + reaches_end = false; + const return_expression: Value.Index = switch (statement_node_index.valid) { + // TODO: expect type + true => ret: { + const return_value_allocation = try analyzer.module.values.addOne(analyzer.allocator); + return_value_allocation.ptr.* = .{ + .unresolved = .{ + .node_index = statement_node.left, + }, + }; + try analyzer.resolveNode(return_value_allocation.ptr, scope_index, ExpectType.none, statement_node.left); + break :ret return_value_allocation.index; + }, + false => @panic("TODO: ret void"), + }; + + const return_value_allocation = try analyzer.module.returns.append(analyzer.allocator, .{ + .value = return_expression, + }); + + const return_expression_value_allocation = try analyzer.module.values.append(analyzer.allocator, .{ + .@"return" = return_value_allocation.index, + }); + + break :blk return_expression_value_allocation.index; + }, else => |t| @panic(@tagName(t)), }; + try statements.append(analyzer.allocator, statement_value); } - return try analyzer.module.blocks.append(analyzer.allocator, .{ + const block_allocation = try analyzer.module.blocks.append(analyzer.allocator, .{ .statements = statements, .reaches_end = reaches_end, }); + + return block_allocation.index; } - fn whileExpression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Loop.Index { - _ = node; - _ = expect_type; - _ = scope; - _ = analyzer; - } - - fn resolve(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, value: *Value) !void { - const node_index = switch (value.*) { - .unresolved => |unresolved| unresolved.node_index, - else => |t| @panic(@tagName(t)), - }; - value.* = try analyzer.resolveNode(scope, expect_type, node_index); - } - - fn doIdentifier(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Value.Index { - assert(node.id == .identifier); - const identifier_hash = try analyzer.identifierFromToken(node.token); + fn doIdentifier(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_token: Token.Index, node_scope_index: Scope.Index) !Value.Index { + const identifier_hash = try analyzer.identifierFromToken(node_scope_index, node_token); + const scope = analyzer.module.scopes.get(scope_index); // TODO: search in upper scopes too const identifier_scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_hash); if (identifier_scope_lookup.found_existing) { const declaration_index = identifier_scope_lookup.value_ptr.*; const declaration = analyzer.module.declarations.get(declaration_index); const init_value = analyzer.module.values.get(declaration.init_value); - try analyzer.resolve(scope, expect_type, init_value); - if (init_value.* != .runtime and declaration.mutability == .@"const") { + print("Declaration found: {}\n", .{init_value}); + switch (init_value.*) { + .unresolved => |ur| try analyzer.resolveNode(init_value, scope_index, expect_type, ur.node_index), + else => {}, + } + if (init_value.isComptime() and declaration.mutability == .@"const") { return declaration.init_value; } else { - unreachable; + const ref_allocation = try analyzer.module.values.append(analyzer.allocator, .{ + .declaration_reference = declaration_index, + }); + return ref_allocation.index; } } else { - @panic("TODO: not found"); + std.debug.panic("Identifier not found in scope #{} of file #{} referenced by scope #{} of file #{}: {s}", .{ scope_index.uniqueInteger(), scope.file.uniqueInteger(), node_scope_index.uniqueInteger(), analyzer.module.scopes.get(node_scope_index).file.uniqueInteger(), tokenBytes(analyzer.getToken(scope_index, node_token), analyzer.getSourceFile(scope_index)) }); } } - fn getArguments(analyzer: *Analyzer, node_index: Node.Index) !ArrayList(Node.Index) { + fn getArguments(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !ArrayList(Node.Index) { var arguments = ArrayList(Node.Index){}; - const node = analyzer.nodes[node_index.unwrap()]; + const node = analyzer.getNode(scope_index, node_index); switch (node.id) { .compiler_intrinsic_two => { try arguments.append(analyzer.allocator, node.left); @@ -214,107 +279,19 @@ const Analyzer = struct { return arguments; } - fn resolveNode(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Value { - const node = analyzer.nodes[node_index.unwrap()]; - return switch (node.id) { - .identifier => unreachable, - .compiler_intrinsic_one, .compiler_intrinsic_two => blk: { - const intrinsic_name = analyzer.tokenIdentifier(node.token + 1); - const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable; - print("Intrinsic: {s}\n", .{@tagName(intrinsic)}); - switch (intrinsic) { - .import => { - assert(node.id == .compiler_intrinsic_one); - const import_argument = analyzer.nodes[node.left.unwrap()]; - switch (import_argument.id) { - .string_literal => { - const import_name = analyzer.tokenStringLiteral(import_argument.token); - const imported_file = try analyzer.module.importFile(analyzer.allocator, analyzer.file, import_name); + fn resolveNode(analyzer: *Analyzer, value: *Value, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) anyerror!void { + const node = analyzer.getNode(scope_index, node_index); + print("Resolving node #{}: {}\n", .{ node_index.uniqueInteger(), node }); - if (imported_file.is_new) { - // TODO: fix error - try analyzer.module.generateAbstractSyntaxTreeForFile(analyzer.allocator, imported_file.file); - } else { - unreachable; - } + assert(value.* == .unresolved); - break :blk .{ - .type = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file), - }; - }, - else => unreachable, - } - }, - .syscall => { - var argument_nodes = try analyzer.getArguments(node_index); - print("Argument count: {}\n", .{argument_nodes.items.len}); - if (argument_nodes.items.len > 0 and argument_nodes.items.len <= 6 + 1) { - const number = try analyzer.expression(scope, ExpectType.none, argument_nodes.items[0]); - assert(number.valid); - var arguments = std.mem.zeroes([6]Value.Index); - for (argument_nodes.items[1..], 0..) |argument_node_index, argument_index| { - const argument = try analyzer.expression(scope, ExpectType.none, argument_node_index); - print("Index: {}. Argument: {}\n", .{ argument_index, argument }); - arguments[argument_index] = argument; - } - - // TODO: typecheck for usize - for (arguments[0..argument_nodes.items.len]) |argument| { - _ = argument; - } - - break :blk .{ - .syscall = try analyzer.module.syscalls.append(analyzer.allocator, .{ - .number = number, - .arguments = arguments, - .argument_count = @intCast(argument_nodes.items.len - 1), - }), - }; - } else { - unreachable; - } - }, - } - unreachable; + value.* = switch (node.id) { + .identifier => blk: { + const value_index = try analyzer.doIdentifier(scope_index, expect_type, node.token, scope_index); + const value_ref = analyzer.module.values.get(value_index); + break :blk value_ref.*; }, - .function_definition => blk: { - const function_prototype_index = try analyzer.functionPrototype(node.left); - - const function_body = try analyzer.block(scope, .{ - .type_index = analyzer.functionPrototypeReturnType(function_prototype_index), - }, node.right); - - const function_index = try analyzer.module.functions.append(analyzer.allocator, .{ - .prototype = function_prototype_index, - .body = function_body, - }); - break :blk .{ - .function = function_index, - }; - }, - .keyword_true => unreachable, - .simple_while => unreachable, - .block_zero, .block_one => blk: { - const block_index = try analyzer.block(scope, expect_type, node_index); - break :blk .{ - .block = block_index, - }; - }, - .number_literal => switch (std.zig.parseNumberLiteral(analyzer.tokenBytes(analyzer.tokens[node.token]))) { - .int => |integer| .{ - .integer = integer, - }, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - }; - } - - fn expression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) !Value.Index { - const node = analyzer.nodes[node_index.unwrap()]; - return switch (node.id) { - .identifier => analyzer.doIdentifier(scope, expect_type, node), - .keyword_true => blk: { + .keyword_true => { switch (expect_type) { .none => {}, .type_index => |expected_type| { @@ -324,9 +301,140 @@ const Analyzer = struct { }, } - break :blk Values.getIndex(.bool_true); + // TODO + unreachable; + + // break :blk Values.getIndex(.bool_true); }, - else => try analyzer.module.values.append(analyzer.allocator, try analyzer.resolveNode(scope, expect_type, node_index)), + .compiler_intrinsic_one, .compiler_intrinsic_two => blk: { + const intrinsic_name = analyzer.tokenIdentifier(scope_index, node.token + 1); + const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable; + print("Intrinsic: {s}\n", .{@tagName(intrinsic)}); + switch (intrinsic) { + .import => { + assert(node.id == .compiler_intrinsic_one); + const import_argument = analyzer.getNode(scope_index, node.left); + switch (import_argument.id) { + .string_literal => { + const import_name = analyzer.tokenStringLiteral(scope_index, import_argument.token); + const import_file = try analyzer.module.importFile(analyzer.allocator, analyzer.current_file, import_name); + + if (import_file.file.is_new) { + // TODO: fix error + try analyzer.module.generateAbstractSyntaxTreeForFile(analyzer.allocator, import_file.file.ptr); + } else { + unreachable; + } + + break :blk .{ + .type = try analyzeFile(value, analyzer.allocator, analyzer.module, import_file.file.ptr, import_file.file.index), + }; + }, + else => unreachable, + } + }, + .syscall => { + var argument_nodes = try analyzer.getArguments(scope_index, node_index); + print("Argument count: {}\n", .{argument_nodes.items.len}); + if (argument_nodes.items.len > 0 and argument_nodes.items.len <= 6 + 1) { + const number_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, argument_nodes.items[0]); + const number = number_allocation.index; + assert(number.valid); + var arguments = std.mem.zeroes([6]Value.Index); + for (argument_nodes.items[1..], 0..) |argument_node_index, argument_index| { + const argument_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, argument_node_index); + arguments[argument_index] = argument_allocation.index; + } + + // TODO: typecheck for usize + for (arguments[0..argument_nodes.items.len]) |argument| { + _ = argument; + } + + break :blk .{ + .syscall = (try analyzer.module.syscalls.append(analyzer.allocator, .{ + .number = number, + .arguments = arguments, + .argument_count = @intCast(argument_nodes.items.len - 1), + })).index, + }; + } else { + unreachable; + } + }, + } + unreachable; + }, + .function_definition => blk: { + const function_prototype_index = try analyzer.functionPrototype(scope_index, node.left); + + const function_body = try analyzer.block(scope_index, .{ + .type_index = analyzer.functionPrototypeReturnType(function_prototype_index), + }, node.right); + + const function_allocation = try analyzer.module.functions.append(analyzer.allocator, .{ + .prototype = function_prototype_index, + .body = function_body, + }); + break :blk .{ + .function = function_allocation.index, + }; + }, + .simple_while => unreachable, + .block_zero, .block_one => blk: { + const block_index = try analyzer.block(scope_index, expect_type, node_index); + break :blk .{ + .block = block_index, + }; + }, + .number_literal => switch (std.zig.parseNumberLiteral(analyzer.numberBytes(scope_index, node.token))) { + .int => |integer| .{ + .integer = integer, + }, + else => |t| @panic(@tagName(t)), + }, + .call_one => blk: { + const this_value_node_index = node.left; + const this_value_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, this_value_node_index); + + const call_allocation = try analyzer.module.calls.append(analyzer.allocator, .{ + .value = this_value_allocation.index, + .arguments = ArgumentList.Index.invalid, + }); + break :blk .{ + .call = call_allocation.index, + }; + }, + .field_access => blk: { + const left_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const identifier = analyzer.tokenIdentifier(scope_index, node.right.value); + _ = identifier; + switch (left_allocation.ptr.*) { + .type => |type_index| { + const left_type = analyzer.module.types.get(type_index); + switch (left_type.*) { + .@"struct" => |struct_index| { + const struct_type = analyzer.module.structs.get(struct_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); + switch (right_value.*) { + .function => break :blk right_value.*, + else => unreachable, + } + print("Right: {}\n", .{right_value}); + // struct_scope.declarations.get(identifier); + + unreachable; + }, + else => |t| @panic(@tagName(t)), + } + unreachable; + }, + else => |t| @panic(@tagName(t)), + } + unreachable; + }, + else => |t| @panic(@tagName(t)), }; } @@ -335,37 +443,55 @@ const Analyzer = struct { return function_prototype.return_type; } - fn functionPrototype(analyzer: *Analyzer, node_index: Node.Index) !Function.Prototype.Index { - const node = analyzer.nodes[node_index.unwrap()]; + fn functionPrototype(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Function.Prototype.Index { + const node = analyzer.getNode(scope_index, node_index); switch (node.id) { .simple_function_prototype => { const arguments: ?[]const Field.Index = blk: { - const argument_node = analyzer.nodes[node.left.get() orelse break :blk null]; + if (node.left.get() == null) break :blk null; + const argument_node = analyzer.getNode(scope_index, node.left); switch (argument_node.id) { else => |t| @panic(@tagName(t)), } }; - const return_type_node = analyzer.nodes[node.right.unwrap()]; + const return_type_node = analyzer.getNode(scope_index, node.right); const return_type: Type.Index = switch (return_type_node.id) { .identifier => { unreachable; }, .keyword_noreturn => .{ .block = 0, .index = FixedTypeKeyword.offset + @intFromEnum(FixedTypeKeyword.noreturn) }, + inline .signed_integer_type, .unsigned_integer_type => |int_type_signedness| blk: { + const bit_count: u16 = @intCast(return_type_node.left.value); + print("Bit count: {}\n", .{bit_count}); + break :blk switch (bit_count) { + inline 8, 16, 32, 64 => |hardware_bit_count| Type.Index{ + .block = 0, + .index = @ctz(hardware_bit_count) - @ctz(@as(u8, 8)) + switch (int_type_signedness) { + .signed_integer_type => HardwareSignedIntegerType, + .unsigned_integer_type => HardwareUnsignedIntegerType, + else => unreachable, + }.offset, + }, + else => unreachable, + }; + }, else => |t| @panic(@tagName(t)), }; - return try analyzer.module.function_prototypes.append(analyzer.allocator, .{ + const function_prototype_allocation = try analyzer.module.function_prototypes.append(analyzer.allocator, .{ .arguments = arguments, .return_type = return_type, }); + + return function_prototype_allocation.index; }, else => |t| @panic(@tagName(t)), } } - fn analyzeDeclaration(analyzer: *Analyzer, scope: *Scope, declaration: *Declaration) !Value.Index { + fn analyzeDeclaration(analyzer: *Analyzer, scope_index: Scope.Index, declaration: *Declaration) !Value.Index { + _ = scope_index; _ = declaration; - _ = scope; _ = analyzer; // switch (declaration.*) { // .unresolved => |node_index| { @@ -394,125 +520,157 @@ const Analyzer = struct { @panic("TODO: analyzeDeclaration"); } - fn structType(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Type.Index { - _ = index; - const new_scope = try analyzer.allocateScope(.{ .parent = parent_scope }); - const scope = new_scope.ptr; - - const is_file = !parent_scope.valid; - assert(is_file); - - const struct_index = try analyzer.module.structs.append(analyzer.allocator, .{ - .scope = new_scope.index, - }); - const struct_type = analyzer.module.structs.get(struct_index); - const type_index = try analyzer.module.types.append(analyzer.allocator, .{ - .@"struct" = struct_index, - }); - scope.type = type_index; - - _ = struct_type; - assert(container_declaration.members.len > 0); - - const count = blk: { - var result: struct { - fields: u32 = 0, - declarations: u32 = 0, - } = .{}; - for (container_declaration.members) |member_index| { - const member = analyzer.nodes[member_index.unwrap()]; - const member_type = getContainerMemberType(member.id); - - switch (member_type) { - .declaration => result.declarations += 1, - .field => result.fields += 1, - } - } - break :blk result; + fn structType(analyzer: *Analyzer, value: *Value, parent_scope_index: Scope.Index, index: Node.Index, file_index: File.Index) !Type.Index { + var node_buffer: [2]Node.Index = undefined; + // We have the file because this might be the first file + const file = analyzer.module.files.get(file_index); + const node = file.syntactic_analyzer_result.nodes.items[index.unwrap()]; + const nodes = switch (node.id) { + .main_one => blk: { + node_buffer[0] = node.left; + break :blk node_buffer[0..1]; + }, + .main_two => blk: { + node_buffer[0] = node.left; + node_buffer[1] = node.right; + break :blk &node_buffer; + }, + else => |t| @panic(@tagName(t)), }; - var declaration_nodes = try ArrayList(Node.Index).initCapacity(analyzer.allocator, count.declarations); - var field_nodes = try ArrayList(Node.Index).initCapacity(analyzer.allocator, count.fields); + if (nodes.len > 0) { + const new_scope = try analyzer.allocateScope(.{ + .parent = parent_scope_index, + .file = file_index, + }); + const scope = new_scope.ptr; + const scope_index = new_scope.index; - for (container_declaration.members) |member_index| { - const member = analyzer.nodes[member_index.unwrap()]; - const member_type = getContainerMemberType(member.id); - const array_list = switch (member_type) { - .declaration => &declaration_nodes, - .field => &field_nodes, + const is_file = !parent_scope_index.valid; + assert(is_file); + + const struct_allocation = try analyzer.module.structs.append(analyzer.allocator, .{ + .scope = new_scope.index, + }); + const type_allocation = try analyzer.module.types.append(analyzer.allocator, .{ + .@"struct" = struct_allocation.index, + }); + scope.type = type_allocation.index; + value.* = .{ + .type = type_allocation.index, }; - array_list.appendAssumeCapacity(member_index); + + const count = blk: { + var result: struct { + fields: u32 = 0, + declarations: u32 = 0, + } = .{}; + for (nodes) |member_index| { + const member = analyzer.getNode(scope_index, member_index); + const member_type = getContainerMemberType(member.id); + + switch (member_type) { + .declaration => result.declarations += 1, + .field => result.fields += 1, + } + } + break :blk result; + }; + + var declaration_nodes = try ArrayList(Node.Index).initCapacity(analyzer.allocator, count.declarations); + var field_nodes = try ArrayList(Node.Index).initCapacity(analyzer.allocator, count.fields); + + for (nodes) |member_index| { + const member = analyzer.getNode(scope_index, member_index); + const member_type = getContainerMemberType(member.id); + const array_list = switch (member_type) { + .declaration => &declaration_nodes, + .field => &field_nodes, + }; + array_list.appendAssumeCapacity(member_index); + } + + for (declaration_nodes.items) |declaration_node_index| { + const declaration_node = analyzer.getNode(scope_index, declaration_node_index); + switch (declaration_node.id) { + .@"comptime" => {}, + .simple_variable_declaration => _ = try analyzer.symbolDeclaration(scope_index, declaration_node_index, .global), + else => unreachable, + } + } + + // TODO: consider iterating over scope declarations instead? + for (declaration_nodes.items) |declaration_node_index| { + const declaration_node = analyzer.getNode(scope_index, declaration_node_index); + switch (declaration_node.id) { + .@"comptime" => _ = try analyzer.comptimeBlock(scope_index, declaration_node_index), + .simple_variable_declaration => {}, + else => |t| @panic(@tagName(t)), + } + } + + for (field_nodes.items) |field_index| { + const field_node = analyzer.getNode(scope_index, field_index); + _ = field_node; + + @panic("TODO: fields"); + } + + return type_allocation.index; + } else { + return Type.Index.invalid; + } + } + + fn symbolDeclaration(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index, scope_type: ScopeType) !Declaration.Index { + const declaration_node = analyzer.getNode(scope_index, node_index); + assert(declaration_node.id == .simple_variable_declaration); + assert(!declaration_node.left.valid); + const mutability: Compilation.Mutability = switch (analyzer.getToken(scope_index, declaration_node.token).id) { + .fixed_keyword_const => .@"const", + .fixed_keyword_var => .@"var", + else => |t| @panic(@tagName(t)), + }; + const expected_identifier_token_index = declaration_node.token + 1; + const expected_identifier_token = analyzer.getToken(scope_index, expected_identifier_token_index); + if (expected_identifier_token.id != .identifier) { + print("Error: found: {}", .{expected_identifier_token.id}); + @panic("Expected identifier"); + } + // TODO: Check if it is a keyword + + const identifier_index = try analyzer.identifierFromToken(scope_index, expected_identifier_token_index); + + const declaration_name = analyzer.tokenIdentifier(scope_index, expected_identifier_token_index); + // Check if the symbol name is already occupied in the same scope + const scope = analyzer.module.scopes.get(scope_index); + const scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_index); + if (scope_lookup.found_existing) { + std.debug.panic("Existing name in lookup: {s}", .{declaration_name}); } - for (declaration_nodes.items) |declaration_node_index| { - const declaration_node = analyzer.nodes[declaration_node_index.unwrap()]; - switch (declaration_node.id) { - .@"comptime" => {}, - .simple_variable_declaration => { - const mutability: Compilation.Mutability = switch (analyzer.tokens[declaration_node.token].id) { - .fixed_keyword_const => .@"const", - .fixed_keyword_var => .@"var", - else => |t| @panic(@tagName(t)), - }; - const expected_identifier_token_index = declaration_node.token + 1; - const expected_identifier_token = analyzer.tokens[expected_identifier_token_index]; - if (expected_identifier_token.id != .identifier) { - print("Error: found: {}", .{expected_identifier_token.id}); - @panic("Expected identifier"); - } - // TODO: Check if it is a keyword + // Check if the symbol name is already occupied in parent scopes + var upper_scope_index = scope.parent; - const identifier_index = try analyzer.identifierFromToken(expected_identifier_token_index); + while (upper_scope_index.valid) { + @panic("TODO: upper scope"); + } + assert(declaration_node.right.valid); - const declaration_name = analyzer.tokenIdentifier(expected_identifier_token_index); - // Check if the symbol name is already occupied in the same scope - const scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_index); - if (scope_lookup.found_existing) { - std.debug.panic("Existing name in lookup: {s}", .{declaration_name}); - } - - // Check if the symbol name is already occupied in parent scopes - var upper_scope_index = scope.parent; - - while (upper_scope_index.valid) { - @panic("TODO: upper scope"); - } - - const container_declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{ - .name = declaration_name, - .scope_type = .global, - .mutability = mutability, - .init_value = try analyzer.module.values.append(analyzer.allocator, .{ - .unresolved = .{ - .node_index = declaration_node.right, - }, - }), - }); - - scope_lookup.value_ptr.* = container_declaration_index; + const declaration_allocation = try analyzer.module.declarations.append(analyzer.allocator, .{ + .name = declaration_name, + .scope_type = scope_type, + .mutability = mutability, + .init_value = (try analyzer.module.values.append(analyzer.allocator, .{ + .unresolved = .{ + .node_index = declaration_node.right, }, - else => unreachable, - } - } + })).index, + }); - // TODO: consider iterating over scope declarations instead? - for (declaration_nodes.items) |declaration_node_index| { - const declaration_node = analyzer.nodes[declaration_node_index.unwrap()]; - switch (declaration_node.id) { - .@"comptime" => _ = try analyzer.comptimeBlock(scope, declaration_node_index), - .simple_variable_declaration => {}, - else => |t| @panic(@tagName(t)), - } - } + scope_lookup.value_ptr.* = declaration_allocation.index; - for (field_nodes.items) |field_index| { - const field_node = analyzer.nodes[field_index.unwrap()]; - _ = field_node; - - @panic("TODO: fields"); - } - - return type_index; + return declaration_allocation.index; } const MemberType = enum { @@ -528,8 +686,8 @@ const Analyzer = struct { }; } - fn identifierFromToken(analyzer: *Analyzer, token_index: Token.Index) !u32 { - const identifier = analyzer.tokenIdentifier(token_index); + fn identifierFromToken(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index) !u32 { + const identifier = analyzer.tokenIdentifier(scope_index, token_index); const key: u32 = @truncate(std.hash.Wyhash.hash(0, identifier)); const lookup_result = try analyzer.module.string_table.getOrPut(analyzer.allocator, key); @@ -541,40 +699,40 @@ const Analyzer = struct { } } - fn tokenIdentifier(analyzer: *Analyzer, token_index: Token.Index) []const u8 { - const token = analyzer.tokens[token_index]; + fn tokenIdentifier(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index) []const u8 { + const token = analyzer.getToken(scope_index, token_index); assert(token.id == .identifier); - const identifier = analyzer.tokenBytes(token); + const source_file = analyzer.getSourceFile(scope_index); + const identifier = tokenBytes(token, source_file); return identifier; } - fn tokenBytes(analyzer: *Analyzer, token: Token) []const u8 { - return analyzer.source_code[token.start..][0..token.len]; + fn tokenBytes(token: Token, source_code: []const u8) []const u8 { + return source_code[token.start..][0..token.len]; } - fn tokenStringLiteral(analyzer: *Analyzer, token_index: Token.Index) []const u8 { - const token = analyzer.tokens[token_index]; + fn numberBytes(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index) []const u8 { + const token = analyzer.getToken(scope_index, token_index); + assert(token.id == .number_literal); + const source_file = analyzer.getSourceFile(scope_index); + const bytes = tokenBytes(token, source_file); + + return bytes; + } + + fn tokenStringLiteral(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index) []const u8 { + const token = analyzer.getToken(scope_index, token_index); assert(token.id == .string_literal); + const source_file = analyzer.getSourceFile(scope_index); // Eat double quotes - const string_literal = analyzer.tokenBytes(token)[1..][0 .. token.len - 2]; + const string_literal = tokenBytes(token, source_file)[1..][0 .. token.len - 2]; return string_literal; } - const ScopeAllocation = struct { - ptr: *Scope, - index: Scope.Index, - }; - - fn allocateScope(analyzer: *Analyzer, scope_value: Scope) !ScopeAllocation { - const scope_index = try analyzer.module.scopes.append(analyzer.allocator, scope_value); - const scope = analyzer.module.scopes.get(scope_index); - - return .{ - .ptr = scope, - .index = scope_index, - }; + fn allocateScope(analyzer: *Analyzer, scope_value: Scope) !Scope.Allocation { + return analyzer.module.scopes.append(analyzer.allocator, scope_value); } }; @@ -647,7 +805,8 @@ const HardwareSignedIntegerType = enum { const offset = HardwareUnsignedIntegerType.offset + @typeInfo(HardwareUnsignedIntegerType).Enum.fields.len; }; -pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Type.Index { +pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, file_index: File.Index) !Type.Index { + _ = file_index; inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { _ = try module.types.append(compilation.base_allocator, @unionInit(Type, enum_field.name, {})); } @@ -692,58 +851,40 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) .@"unreachable" = {}, }); - return analyzeExistingPackage(compilation, module, package); + const value_allocation = try module.values.append(compilation.base_allocator, .{ + .unresolved = .{ + .node_index = .{ .value = 0 }, + }, + }); + + return analyzeExistingPackage(value_allocation.ptr, compilation, module, package); } -pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, package: *Package) !Type.Index { +pub fn analyzeExistingPackage(value: *Value, compilation: *Compilation, module: *Module, package: *Package) !Type.Index { const package_import = try module.importPackage(compilation.base_allocator, package); - assert(!package_import.is_new); - const package_file = package_import.file; + assert(!package_import.file.is_new); + const package_file = package_import.file.ptr; + const file_index = package_import.file.index; - return try analyzeFile(compilation.base_allocator, module, package_file); + return try analyzeFile(value, compilation.base_allocator, module, package_file, file_index); } -pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Type.Index { +pub fn analyzeFile(value: *Value, allocator: Allocator, module: *Module, file: *File, file_index: File.Index) !Type.Index { + assert(value.* == .unresolved); assert(file.status == .parsed); var analyzer = Analyzer{ - .source_code = file.source_code, - .nodes = file.syntactic_analyzer_result.nodes.items, - .tokens = file.lexical_analyzer_result.tokens.items, - .file = file, + .current_file = file_index, .allocator = allocator, .module = module, }; - const result = try analyzer.structType(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 }); + var buffer = [2]Node.Index{ + Node.Index.invalid, + Node.Index.invalid, + }; + _ = buffer; + + const result = try analyzer.structType(value, Scope.Index.invalid, .{ .value = 0 }, file_index); return result; } - -fn mainNodeToContainerDeclaration(allocator: Allocator, file: *File) !ContainerDeclaration { - const main_node = getNode(file, 0); - var list_buffer: [2]Node.Index = undefined; - const left_node = getNode(file, main_node.left.value); - const node_list: []const Node.Index = blk: { - if (left_node.id != .node_list) { - const len = @as(u2, @intFromBool(main_node.left.valid)) + @as(u2, @intFromBool(main_node.right.valid)) - @as(u2, @intFromBool(main_node.left.valid and main_node.right.valid and main_node.left.value == main_node.right.value)); - assert(len > 0); - list_buffer[0] = main_node.left; - list_buffer[1] = main_node.right; - break :blk list_buffer[0..len]; - } else { - @panic("TODO: get list"); - } - }; - - const owned_node_list = try allocator.alloc(Node.Index, node_list.len); - @memcpy(owned_node_list, node_list); - - // Deal properly with this allocation - return .{ - .members = owned_node_list, - }; -} - -fn getNode(file: *const File, index: u32) *Node { - return &file.syntactic_analyzer_result.nodes.items[index]; -} diff --git a/src/frontend/syntactic_analyzer.zig b/src/frontend/syntactic_analyzer.zig index a6b00a7..5efc621 100644 --- a/src/frontend/syntactic_analyzer.zig +++ b/src/frontend/syntactic_analyzer.zig @@ -14,6 +14,7 @@ const Token = lexical_analyzer.Token; pub const Result = struct { nodes: ArrayList(Node), + node_lists: ArrayList(Node.List), time: u64, }; @@ -47,6 +48,11 @@ pub const Node = packed struct(u128) { assert(index.valid); return index.value; } + + pub fn uniqueInteger(index: Index) u32 { + assert(index.valid); + return index.value; + } }; pub const Range = struct { @@ -81,6 +87,15 @@ pub const Node = packed struct(u128) { comptime_block_two = 23, block_two = 24, @"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 { - assert(token.id == .identifier); - const identifier = analyzer.file[token.start..][0..token.len]; - return identifier; + fn bytes(analyzer: *const Analyzer, token_index: Token.Index) []const u8 { + const token = analyzer.tokens[token_index]; + return analyzer.file[token.start..][0..token.len]; + } + + 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 { @@ -121,58 +163,26 @@ const Analyzer = struct { while (analyzer.token_i < analyzer.tokens.len) { 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) { .left_brace => blk: { analyzer.token_i += 1; const comptime_block = try analyzer.block(.{ .is_comptime = true }); - break :blk .{ + break :blk try analyzer.addNode(.{ .id = .@"comptime", .token = first, .left = comptime_block, .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: { - analyzer.token_i += 1; - _ = 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)}), + .fixed_keyword_const, .fixed_keyword_var => try analyzer.symbolDeclaration(), + else => |t| @panic(@tagName(t)), }; - const member_index = try analyzer.addNode(member_node); - try analyzer.temporal_node_heap.append(analyzer.allocator, member_index); + try analyzer.temporal_node_heap.append(analyzer.allocator, member_node_index); } const members_array = analyzer.temporal_node_heap.items[node_heap_top..]; @@ -263,10 +273,12 @@ const Analyzer = struct { }, 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), - 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); } @@ -301,7 +313,15 @@ const Analyzer = struct { .left = statement_array[0], .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); } @@ -329,7 +349,7 @@ const Analyzer = struct { const expression_id: Node.Id = switch (analyzer.tokens[analyzer.token_i].id) { .semicolon => return expr, .equal => .assign, - else => unreachable, + else => |t| @panic(@tagName(t)), }; const node = Node{ @@ -398,8 +418,8 @@ const Analyzer = struct { while (analyzer.token_i < analyzer.tokens.len) { const precedence: i32 = switch (analyzer.tokens[analyzer.token_i].id) { - .equal, .semicolon, .right_parenthesis, .right_brace, .comma => -1, - else => |foo| std.debug.panic("Foo: ({s}) {}", .{ @tagName(foo), foo }), + .equal, .semicolon, .right_parenthesis, .right_brace, .comma, .period => -1, + else => |t| @panic(@tagName(t)), }; 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(), .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:? // .left_brace => try analyzer.block(), else => |id| { @@ -492,14 +522,8 @@ const Analyzer = struct { fn typeExpression(analyzer: *Analyzer) !Node.Index { return switch (analyzer.tokens[analyzer.token_i].id) { - .identifier, .fixed_keyword_noreturn, .fixed_keyword_true, .fixed_keyword_false, .hash => try analyzer.errorUnionExpression(), - else => |id| blk: { - log.warn("By default, calling errorUnionExpression with {s}", .{@tagName(id)}); - - const result = try analyzer.errorUnionExpression(); - - break :blk result; - }, + .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| @panic(@tagName(id)), }; } @@ -516,14 +540,17 @@ const Analyzer = struct { var result = try analyzer.primaryTypeExpression(); while (true) { - if (analyzer.suffixOperator()) |_| { - unreachable; + const suffix_operator = try analyzer.suffixOperator(result); + if (suffix_operator.valid) { + result = suffix_operator; } else { if (analyzer.tokens[analyzer.token_i].id == .left_parenthesis) { + const left_parenthesis = analyzer.token_i; analyzer.token_i += 1; var expression_list = ArrayList(Node.Index){}; while (analyzer.tokens[analyzer.token_i].id != .right_parenthesis) { + std.debug.print("Loop\n", .{}); const parameter = try analyzer.expression(); try expression_list.append(analyzer.allocator, parameter); analyzer.token_i += @intFromBool(switch (analyzer.tokens[analyzer.token_i].id) { @@ -534,7 +561,16 @@ const Analyzer = struct { } _ = 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 { return result; } @@ -569,8 +605,8 @@ const Analyzer = struct { .identifier => switch (analyzer.tokens[token_i + 1].id) { .colon => unreachable, else => blk: { - const identifier = analyzer.getIdentifier(token); - std.debug.print("identifier: {s}\n", .{identifier}); + const identifier = analyzer.bytes(token_i); + // std.debug.print("identifier: {s}\n", .{identifier}); analyzer.token_i += 1; if (equal(u8, identifier, "_")) { break :blk Node.Index.invalid; @@ -594,20 +630,55 @@ const Analyzer = struct { .right = Node.Index.invalid, }), .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| { switch (foo) { - .identifier => std.debug.panic("{s}: {s}", .{ @tagName(foo), analyzer.getIdentifier(analyzer.tokens[token_i]) }), - else => std.debug.panic("{s}", .{@tagName(foo)}), + .identifier => std.debug.panic("{s}: {s}", .{ @tagName(foo), analyzer.bytes(token_i) }), + else => @panic(@tagName(foo)), } }, }; } // TODO: - fn suffixOperator(analyzer: *Analyzer) ?bool { - _ = analyzer; - - return null; + fn suffixOperator(analyzer: *Analyzer, left: Node.Index) !Node.Index { + const token = analyzer.tokens[analyzer.token_i]; + return switch (token.id) { + .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 { @@ -618,27 +689,23 @@ const Analyzer = struct { .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 { len: usize, left: 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 { @@ -657,10 +724,22 @@ pub fn analyze(allocator: Allocator, tokens: []const Token, file: []const u8) !R assert(node_index.value == 0); assert(node_index.valid); + const members = try analyzer.containerMembers(); - const member_range = members.toRange(); - analyzer.nodes.items[0].left = .{ .value = @intCast(member_range.start) }; - analyzer.nodes.items[0].right = .{ .value = @intCast(member_range.end) }; + + switch (members.len) { + 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; @@ -668,6 +747,7 @@ pub fn analyze(allocator: Allocator, tokens: []const Token, file: []const u8) !R return .{ .nodes = analyzer.nodes, + .node_lists = analyzer.node_lists, .time = end.since(start), }; } diff --git a/src/fs.zig b/src/fs.zig index c8c5963..a8ec0ec 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -1,8 +1,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -pub const first = "src/test/main.b"; - 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)); return file; diff --git a/src/main.zig b/src/main.zig index 93052c3..6ddc0b3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,7 +5,7 @@ const assert = std.debug.assert; const Compilation = @import("Compilation.zig"); 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 { try singleCompilation(default_src_file); diff --git a/src/test/main.nat b/src/test/main.nat index 157bd8a..45bfaac 100644 --- a/src/test/main.nat +++ b/src/test/main.nat @@ -1,3 +1,3 @@ -const main = fn() i32 { +const main = fn() s32 { return 0; };