From 48c3b5e2241f2558e150aba50faf71dff0bb2175 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Mon, 18 Sep 2023 07:31:26 -0600 Subject: [PATCH] ir --- .vscode/launch.json | 17 +- src/Compilation.zig | 97 ++++- src/backend/emit.zig | 185 +++++++++- src/backend/intermediate_representation.zig | 242 +++++++++++- src/backend/x86_64.zig | 0 src/data_structures.zig | 49 ++- src/frontend/semantic_analyzer.zig | 389 ++++++++++++-------- src/frontend/syntactic_analyzer.zig | 81 ++-- 8 files changed, 822 insertions(+), 238 deletions(-) create mode 100644 src/backend/x86_64.zig diff --git a/.vscode/launch.json b/.vscode/launch.json index 27965bb..46a561c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,13 +5,22 @@ "version": "0.2.0", "configurations": [ { - "type": "cppvsdbg", + "type": "lldb", "request": "launch", - "name": "Debug", - "program": "${workspaceFolder}/zig-out/bin/compiler.exe", + "name": "Launch", + "program": "${workspaceFolder}/zig-out/bin/compiler", "args": [], "cwd": "${workspaceFolder}", "preLaunchTask": "zig build" - } + }, + // { + // "type": "cppvsdbg", + // "request": "launch", + // "name": "Debug", + // "program": "${workspaceFolder}/zig-out/bin/compiler.exe", + // "args": [], + // "cwd": "${workspaceFolder}", + // "preLaunchTask": "zig build" + // } ] } \ No newline at end of file diff --git a/src/Compilation.zig b/src/Compilation.zig index 8c4f3e9..f471fcc 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -21,6 +21,7 @@ const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig"); const Node = syntactic_analyzer.Node; const semantic_analyzer = @import("frontend/semantic_analyzer.zig"); const intermediate_representation = @import("backend/intermediate_representation.zig"); +const emit = @import("backend/emit.zig"); test { _ = lexical_analyzer; @@ -59,7 +60,8 @@ pub fn init(allocator: Allocator) !*Compilation { pub const Struct = struct { scope: Scope.Index, - initialization: Value.Index, + fields: ArrayList(Field.Index) = .{}, + pub const List = BlockList(@This()); pub const Index = List.Index; }; @@ -69,6 +71,7 @@ pub const Type = union(enum) { noreturn, bool, integer: Integer, + @"struct": Struct.Index, pub const List = BlockList(@This()); pub const Index = List.Index; }; @@ -85,16 +88,29 @@ pub const Integer = struct { /// A scope contains a bunch of declarations pub const Scope = struct { parent: Scope.Index, - type: Type.Index, + type: Type.Index = Type.Index.invalid, declarations: AutoHashMap(u32, Declaration.Index) = .{}, pub const List = BlockList(@This()); pub const Index = List.Index; }; -pub const Declaration = union(enum) { - unresolved: Node.Index, - struct_type: Struct, +pub const ScopeType = enum(u1) { + local = 0, + global = 1, +}; + +pub const Mutability = enum(u1) { + @"const", + @"var", +}; + +pub const Declaration = struct { + scope_type: ScopeType, + mutability: Mutability, + init_value: Value.Index, + name: []const u8, + pub const List = BlockList(@This()); pub const Index = List.Index; }; @@ -111,12 +127,17 @@ pub const Function = struct { pub const Index = Prototype.List.Index; }; + pub fn getBodyBlock(function: Function, module: *Module) *Block { + return module.blocks.get(function.body); + } + pub const List = BlockList(@This()); pub const Index = List.Index; }; pub const Block = struct { - foo: u32 = 0, + statements: ArrayList(Value.Index) = .{}, + reaches_end: bool, pub const List = BlockList(@This()); pub const Index = List.Index; }; @@ -129,27 +150,61 @@ pub const Field = struct { }; pub const Loop = struct { - foo: u32 = 0, + condition: Value.Index, + body: Value.Index, + breaks: bool, pub const List = BlockList(@This()); pub const Index = List.Index; }; -pub const Value = struct { - type: union(enum) { - declaration: Declaration.Index, - bool_true, - bool_false, - loop: Loop.Index, - function: Function.Index, - }, - is_const: bool, - is_comptime: bool, +const Runtime = struct { + foo: u32 = 0, +}; + +const Unresolved = struct { + node_index: Node.Index, +}; + +pub const Assignment = struct { + store: Value.Index, + load: Value.Index, pub const List = BlockList(@This()); pub const Index = List.Index; }; +pub const Value = union(enum) { + unresolved: Unresolved, + declaration: Declaration.Index, + void, + bool: bool, + undefined, + loop: Loop.Index, + function: Function.Index, + block: Block.Index, + runtime: Runtime, + assign: Assignment.Index, + type: Type.Index, + + pub const List = BlockList(@This()); + pub const Index = List.Index; + + pub fn isComptime(value: Value) bool { + return switch (value) { + .bool, .void, .undefined, .function => true, + else => false, + }; + } + + pub fn getType(value: *Value) !void { + switch (value.*) { + else => |t| @panic(@tagName(t)), + } + unreachable; + } +}; + pub const Module = struct { main_package: *Package, import_table: StringArrayHashMap(*File) = .{}, @@ -165,6 +220,7 @@ pub const Module = struct { types: BlockList(Type) = .{}, blocks: BlockList(Block) = .{}, loops: BlockList(Loop) = .{}, + assignments: BlockList(Assignment) = .{}, pub const Descriptor = struct { main_package_path: []const u8, @@ -354,7 +410,12 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0]); - try intermediate_representation.initialize(compilation, module, packages[0], main_declaration); + var ir = try intermediate_representation.initialize(compilation, module, packages[0], main_declaration); + + switch (@import("builtin").cpu.arch) { + .x86_64 => |arch| try emit.get(arch).initialize(compilation.base_allocator, &ir), + else => {}, + } } fn generateAST() !void {} diff --git a/src/backend/emit.zig b/src/backend/emit.zig index 17b708d..a8f652f 100644 --- a/src/backend/emit.zig +++ b/src/backend/emit.zig @@ -6,7 +6,13 @@ const assert = std.debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -const ir = @import("ir.zig"); +const ir = @import("intermediate_representation.zig"); + +const data_structures = @import("../data_structures.zig"); +const ArrayList = data_structures.ArrayList; +const AutoHashMap = data_structures.AutoHashMap; + +const jit_callconv = .SysV; const Section = struct { content: []align(page_size) u8, @@ -39,8 +45,13 @@ const Result = struct { const windows = std.os.windows; break :blk @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE))))[0..size]; }, - .linux => blk: { - const protection_flags = std.os.PROT.READ | std.os.PROT.WRITE | if (flags.executable) std.os.PROT.EXEC else 0; + .linux, .macos => |os_tag| blk: { + const execute_flag: switch (os_tag) { + .linux => u32, + .macos => c_int, + else => unreachable, + } = if (flags.executable) std.os.PROT.EXEC else 0; + const protection_flags: u32 = @intCast(std.os.PROT.READ | std.os.PROT.WRITE | execute_flag); const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE; break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0); @@ -60,16 +71,163 @@ const Result = struct { image.sections.text.index += 1; } - fn getEntryPoint(image: *const Result, comptime Function: type) *const Function { + fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType { comptime { - assert(@typeInfo(Function) == .Fn); + assert(@typeInfo(FunctionType) == .Fn); } assert(image.sections.text.content.len > 0); - return @as(*const Function, @ptrCast(&image.sections.text.content[image.entry_point])); + return @as(*const FunctionType, @ptrCast(&image.sections.text.content[image.entry_point])); } }; +const SimpleRelocation = struct { + source: u32, + write_offset: u8, + instruction_len: u8, + size: u8, +}; + +const RelocationManager = struct { + in_function_relocations: data_structures.AutoHashMap(ir.BasicBlock.Index, ArrayList(SimpleRelocation)) = .{}, +}; + +pub fn get(comptime arch: std.Target.Cpu.Arch) type { + const backend = switch (arch) { + .x86_64 => @import("x86_64.zig"), + else => @compileError("Architecture not supported"), + }; + _ = backend; + const Function = struct { + block_byte_counts: ArrayList(u16), + byte_count: u32 = 0, + relocations: ArrayList(Relocation) = .{}, + block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{}, + const Relocation = struct { + source: ir.BasicBlock.Index, + destination: ir.BasicBlock.Index, + offset_offset: u8, + preferred_instruction_len: u8, + }; + }; + + const InstructionSelector = struct { + functions: ArrayList(Function), + }; + _ = InstructionSelector; + + return struct { + pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void { + _ = intermediate; + _ = allocator; + // var function_iterator = intermediate.functions.iterator(); + // var instruction_selector = InstructionSelector{ + // .functions = try ArrayList(Function).initCapacity(allocator, intermediate.functions.len), + // }; + // while (function_iterator.next()) |ir_function| { + // const function = instruction_selector.functions.addOneAssumeCapacity(); + // function.* = .{ + // .block_byte_counts = try ArrayList(u16).initCapacity(allocator, ir_function.blocks.items.len), + // }; + // try function.block_map.ensureTotalCapacity(allocator, @intCast(ir_function.blocks.items.len)); + // for (ir_function.blocks.items, 0..) |block_index, index| { + // function.block_map.putAssumeCapacity(allocator, block_index, @intCast(index)); + // } + // + // for (ir_function.blocks.items) |block_index| { + // const block = intermediate.blocks.get(block_index); + // var block_byte_count: u16 = 0; + // for (block.instructions.items) |instruction_index| { + // const instruction = intermediate.instructions.get(instruction_index).*; + // switch (instruction) { + // .phi => unreachable, + // .ret => unreachable, + // .jump => { + // block_byte_count += 2; + // }, + // } + // } + // function.block_byte_counts.appendAssumeCapacity(block_byte_count); + // } + // } + // unreachable; + } + }; +} + +pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void { + _ = allocator; + var result = try Result.create(); + _ = result; + var relocation_manager = RelocationManager{}; + _ = relocation_manager; + + var function_iterator = intermediate.functions.iterator(); + _ = function_iterator; + // while (function_iterator.next()) |function| { + // defer relocation_manager.in_function_relocations.clearRetainingCapacity(); + // + // for (function.blocks.items) |block_index| { + // if (relocation_manager.in_function_relocations.getPtr(block_index)) |relocations| { + // const current_offset: i64 = @intCast(result.sections.text.index); + // _ = current_offset; + // for (relocations.items) |relocation| switch (relocation.size) { + // inline @sizeOf(u8), @sizeOf(u32) => |relocation_size| { + // const Elem = switch (relocation_size) { + // @sizeOf(u8) => u8, + // @sizeOf(u32) => u32, + // else => unreachable, + // }; + // const Ptr = *align(1) Elem; + // _ = Ptr; + // const relocation_slice = result.sections.text.content[relocation.source + relocation.write_offset ..][0..relocation_size]; + // _ = relocation_slice; + // // std.math.cast( + // // + // unreachable; + // }, + // else => unreachable, + // }; + // // const ptr: *align(1) u32 = @ptrCast(&result.sections.text[relocation_source..][0..@sizeOf(u32)]); + // // ptr.* = + // // try relocations.append(allocator, @intCast(result.sections.text[)); + // } + // + // const block = intermediate.blocks.get(block_index); + // + // for (block.instructions.items) |instruction_index| { + // const instruction = intermediate.instructions.get(instruction_index); + // switch (instruction.*) { + // .jump => |jump_index| { + // const jump = intermediate.jumps.get(jump_index); + // assert(@as(u32, @bitCast(jump.source)) == @as(u32, @bitCast(block_index))); + // const relocation_index = result.sections.text.index + 1; + // if (@as(u32, @bitCast(jump.destination)) <= @as(u32, @bitCast(jump.source))) { + // unreachable; + // } else { + // result.appendCode(&(.{jmp_rel_32} ++ .{0} ** @sizeOf(u32))); + // const lookup_result = try relocation_manager.in_function_relocations.getOrPut(allocator, jump.destination); + // if (!lookup_result.found_existing) { + // lookup_result.value_ptr.* = .{}; + // } + // + // try lookup_result.value_ptr.append(allocator, .{ + // .source = @intCast(relocation_index), + // .write_offset = @sizeOf(u8), + // .instruction_len = @sizeOf(u8) + @sizeOf(u32), + // .size = @sizeOf(u32), + // }); + // } + // }, + // else => |t| @panic(@tagName(t)), + // } + // } + // } + // unreachable; + // } + // unreachable; +} + const Rex = enum(u8) { b = upper_4_bits | (1 << 0), x = upper_4_bits | (1 << 1), @@ -115,6 +273,7 @@ const prefix_rep = 0xf3; const prefix_rex_w = [1]u8{@intFromEnum(Rex.w)}; const prefix_16_bit_operand = [1]u8{0x66}; +const jmp_rel_32 = 0xe9; const ret = 0xc3; const mov_a_imm = [1]u8{0xb8}; const mov_reg_imm8: u8 = 0xb0; @@ -142,7 +301,7 @@ test "ret void" { var image = try Result.create(); image.appendCodeByte(ret); - const function_pointer = image.getEntryPoint(fn () callconv(.C) void); + const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) void); function_pointer(); } @@ -167,7 +326,7 @@ test "ret integer" { movAImm(&image, expected_number); image.appendCodeByte(ret); - const function_pointer = image.getEntryPoint(fn () callconv(.C) Int); + const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) Int); const result = function_pointer(); try expect(result == expected_number); } @@ -216,7 +375,7 @@ test "ret integer argument" { movRmR(&image, Int, .a, .di); image.appendCodeByte(ret); - const functionPointer = image.getEntryPoint(fn (Int) callconv(.C) Int); + const functionPointer = image.getEntryPoint(fn (Int) callconv(jit_callconv) Int); const result = functionPointer(number); try expectEqual(number, result); } @@ -246,7 +405,7 @@ test "ret sub arguments" { subRmR(&image, Int, .a, .si); image.appendCodeByte(ret); - const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(.C) Int); + const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(jit_callconv) Int); const result = functionPointer(a, b); try expectEqual(a - b, result); } @@ -328,7 +487,7 @@ fn TestIntegerBinaryOperation(comptime T: type) type { dstRmSrcR(&image, T, test_case.opcode, .a, .si); image.appendCodeByte(ret); - const functionPointer = image.getEntryPoint(fn (T, T) callconv(.C) T); + const functionPointer = image.getEntryPoint(fn (T, T) callconv(jit_callconv) T); const expected = test_case.callback(a, b); const result = functionPointer(a, b); if (should_log) { @@ -350,7 +509,7 @@ test "call after" { @as(*align(1) u32, @ptrCast(&image.sections.text.content[jump_patch_offset])).* = @intCast(jump_target - jump_source); image.appendCodeByte(ret); - const functionPointer = image.getEntryPoint(fn () callconv(.C) void); + const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void); functionPointer(); } @@ -369,7 +528,7 @@ test "call before" { image.appendCode(&second_call); image.appendCodeByte(ret); - const functionPointer = image.getEntryPoint(fn () callconv(.C) void); + const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void); functionPointer(); } diff --git a/src/backend/intermediate_representation.zig b/src/backend/intermediate_representation.zig index 15ed936..501319f 100644 --- a/src/backend/intermediate_representation.zig +++ b/src/backend/intermediate_representation.zig @@ -1,9 +1,243 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const print = std.debug.print; + const Compilation = @import("../Compilation.zig"); const Module = Compilation.Module; const Package = Compilation.Package; -pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_declaration: Compilation.Declaration.Index) !void { - _ = main_declaration; + +const data_structures = @import("../data_structures.zig"); +const ArrayList = data_structures.ArrayList; +const BlockList = data_structures.BlockList; + +pub const Result = struct { + functions: BlockList(Function) = .{}, + blocks: BlockList(BasicBlock) = .{}, + instructions: BlockList(Instruction) = .{}, + jumps: BlockList(Jump) = .{}, +}; + +pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result { + _ = main_file; _ = package; - _ = module; - _ = compilation; + print("\nFunction count: {}\n", .{module.functions.len}); + + var function_iterator = module.functions.iterator(); + var builder = Builder{ + .allocator = compilation.base_allocator, + .module = module, + }; + + while (function_iterator.next()) |sema_function| { + print("\nFunction: {}\n", .{sema_function}); + + try builder.function(sema_function); + } + + return builder.ir; } + +pub const BasicBlock = struct { + instructions: ArrayList(Instruction.Index) = .{}, + incomplete_phis: ArrayList(Instruction.Index) = .{}, + filled: bool = false, + sealed: bool = false, + + pub const List = BlockList(@This()); + pub const Index = List.Index; + + fn seal(basic_block: *BasicBlock) void { + for (basic_block.incomplete_phis.items) |incomplete_phi| { + _ = incomplete_phi; + unreachable; + } + + basic_block.sealed = true; + } +}; + +const Instruction = union(enum) { + jump: Jump.Index, + phi: Phi.Index, + ret: Ret, + + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + +const Phi = struct { + foo: u32 = 0, + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + +const Ret = struct { + value: Instruction.Index, +}; + +pub const Jump = struct { + source: BasicBlock.Index, + destination: BasicBlock.Index, + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + +const Function = struct { + blocks: ArrayList(BasicBlock.Index) = .{}, + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + +pub const Builder = struct { + allocator: Allocator, + ir: Result = .{}, + module: *Module, + current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid, + current_function_index: Function.Index = Function.Index.invalid, + + fn function(builder: *Builder, sema_function: Compilation.Function) !void { + builder.current_function_index = try builder.ir.functions.append(builder.allocator, .{}); + // TODO: arguments + builder.current_basic_block = try builder.newBlock(); + + const return_type = builder.module.types.get(builder.module.function_prototypes.get(sema_function.prototype).return_type); + 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 ret = try builder.appendToBlock(exit_block, .{ + .ret = .{ + .value = phi, + }, + }); + _ = ret; + } + const sema_block = sema_function.getBodyBlock(builder.module); + try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn }); + + try builder.dumpFunction(std.io.getStdErr().writer(), builder.current_function_index); + } + + fn dumpFunction(builder: *Builder, writer: anytype, index: Function.Index) !void { + const f = builder.ir.functions.get(index); + try writer.writeAll("Hello world!\n"); + print("Function blocks: {}\n", .{f.blocks.items.len}); + var function_instruction_index: usize = 0; + for (f.blocks.items, 0..) |block_index, function_block_index| { + print("#{}:\n", .{function_block_index}); + const function_block = builder.ir.blocks.get(block_index); + for (function_block.instructions.items) |instruction_index| { + const instruction = builder.ir.instructions.get(instruction_index); + print("%{}: {}\n", .{ function_instruction_index, instruction }); + function_instruction_index += 1; + } + + print("\n", .{}); + } + } + + fn blockInsideBasicBlock(builder: *Builder, sema_block: *Compilation.Block, block_index: BasicBlock.Index) !BasicBlock.Index { + builder.current_basic_block = block_index; + try builder.block(sema_block, .{}); + return builder.current_basic_block; + } + + const BlockOptions = packed struct { + emit_exit_block: bool = true, + }; + + fn block(builder: *Builder, sema_block: *Compilation.Block, options: BlockOptions) error{OutOfMemory}!void { + for (sema_block.statements.items) |sema_statement_index| { + const sema_statement = builder.module.values.get(sema_statement_index); + switch (sema_statement.*) { + .loop => |loop_index| { + const sema_loop = builder.module.loops.get(loop_index); + const sema_loop_condition = builder.module.values.get(sema_loop.condition); + const sema_loop_body = builder.module.values.get(sema_loop.body); + const condition: Compilation.Value.Index = switch (sema_loop_condition.*) { + .bool => |bool_value| switch (bool_value) { + true => Compilation.Value.Index.invalid, + false => unreachable, + }, + else => |t| @panic(@tagName(t)), + }; + + const original_block = builder.current_basic_block; + const jump_to_loop = try builder.append(.{ + .jump = undefined, + }); + const loop_body_block = try builder.newBlock(); + const loop_prologue_block = if (options.emit_exit_block) try builder.newBlock() else BasicBlock.Index.invalid; + + const loop_head_block = switch (condition.valid) { + false => loop_body_block, + true => unreachable, + }; + + builder.ir.instructions.get(jump_to_loop).jump = try builder.jump(.{ + .source = original_block, + .destination = loop_head_block, + }); + + const sema_body_block = builder.module.blocks.get(sema_loop_body.block); + builder.current_basic_block = try builder.blockInsideBasicBlock(sema_body_block, loop_body_block); + if (loop_prologue_block.valid) { + builder.ir.blocks.get(loop_prologue_block).seal(); + } + + if (sema_body_block.reaches_end) { + _ = try builder.append(.{ + .jump = try builder.jump(.{ + .source = builder.current_basic_block, + .destination = loop_head_block, + }), + }); + } + + builder.ir.blocks.get(builder.current_basic_block).filled = true; + builder.ir.blocks.get(loop_body_block).seal(); + if (!loop_head_block.eq(loop_body_block)) { + unreachable; + } + + if (loop_prologue_block.valid) { + builder.current_basic_block = loop_prologue_block; + } + }, + else => |t| @panic(@tagName(t)), + } + } + } + + 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); + } + + fn append(builder: *Builder, instruction: Instruction) !Instruction.Index { + assert(builder.current_basic_block.valid); + return builder.appendToBlock(builder.current_basic_block, instruction); + } + + 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); + + return instruction_index; + } + + fn newBlock(builder: *Builder) !BasicBlock.Index { + const new_block_index = 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); + + print("Adding block: {}\n", .{function_block_index}); + + return new_block_index; + } +}; diff --git a/src/backend/x86_64.zig b/src/backend/x86_64.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/data_structures.zig b/src/data_structures.zig index 6edf4d2..cc47ff3 100644 --- a/src/data_structures.zig +++ b/src/data_structures.zig @@ -36,17 +36,59 @@ pub fn BlockList(comptime T: type) type { const List = @This(); pub const Index = packed struct(u32) { - valid: bool = true, + block: u24, index: u6, - block: u25, + _reserved: bool = false, + valid: bool = true, pub const invalid = Index{ .valid = false, .index = 0, .block = 0, }; + + pub fn eq(index: Index, other: Index) bool { + return @as(u32, @bitCast(index)) == @as(u32, @bitCast(other)); + } }; + pub const Iterator = struct { + block_index: u26, + element_index: u7, + list: *const List, + + pub fn next(i: *Iterator) ?T { + return if (i.nextPointer()) |ptr| ptr.* else null; + } + + pub fn nextPointer(i: *Iterator) ?*T { + if (i.element_index >= item_count) { + i.block_index += 1; + i.element_index = 0; + } + + while (i.block_index < i.list.blocks.items.len) : (i.block_index += 1) { + while (i.element_index < item_count) : (i.element_index += 1) { + if (i.list.blocks.items[i.block_index].bitset.isSet(i.element_index)) { + const index = i.element_index; + i.element_index += 1; + return &i.list.blocks.items[i.block_index].items[index]; + } + } + } + + return null; + } + }; + + pub fn iterator(list: *const List) Iterator { + return .{ + .block_index = 0, + .element_index = 0, + .list = list, + }; + } + pub fn get(list: *List, index: Index) *T { assert(index.valid); return &list.blocks.items[index.block].items[index.index]; @@ -59,6 +101,7 @@ pub fn BlockList(comptime T: type) type { // 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), @@ -69,8 +112,10 @@ pub fn BlockList(comptime T: type) type { } 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), diff --git a/src/frontend/semantic_analyzer.zig b/src/frontend/semantic_analyzer.zig index 761054b..65be4e4 100644 --- a/src/frontend/semantic_analyzer.zig +++ b/src/frontend/semantic_analyzer.zig @@ -7,10 +7,12 @@ const File = Compilation.File; const Module = Compilation.Module; const Package = Compilation.Package; +const Assignment = Compilation.Assignment; const Block = Compilation.Block; const Declaration = Compilation.Declaration; const Field = Compilation.Field; const Function = Compilation.Function; +const Loop = Compilation.Loop; const Scope = Compilation.Scope; const Struct = Compilation.Struct; const Type = Compilation.Type; @@ -45,90 +47,157 @@ const Analyzer = struct { fn comptimeBlock(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index { const comptime_node = analyzer.nodes[node_index.unwrap()]; - const comptime_block_node = analyzer.nodes[comptime_node.left.unwrap()]; - var statement_node_indices = ArrayList(Node.Index){}; - switch (comptime_block_node.id) { - .block_one => { - try statement_node_indices.append(analyzer.allocator, comptime_block_node.left); + const comptime_block = try analyzer.block(scope, .{ .none = {} }, comptime_node.left); + return try analyzer.module.values.append(analyzer.allocator, .{ + .block = comptime_block, + }); + } + + fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Assignment.Index { + print("Assign: #{}", .{node_index.value}); + const node = analyzer.nodes[node_index.unwrap()]; + assert(node.id == .assign); + const Result = struct { + left: Value.Index, + right: Value.Index, + }; + const result: Result = switch (node.left.valid) { + // In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result``` + false => .{ + .left = Value.Index.invalid, + .right = try analyzer.expression(scope, ExpectType.none, node.right), }, + true => { + const left_node = analyzer.nodes[node.left.unwrap()]; + print("left node index: {}. Left node: {}", .{ node.left, left_node }); + // const id = analyzer.tokenIdentifier(.token); + // print("id: {s}\n", .{id}); + const left = try analyzer.expression(scope, ExpectType.none, node.left); + _ = left; + unreachable; + }, + }; + + print("Assignment: L: {}. R: {}\n", .{ result.left, result.right }); + + if (result.left.valid and analyzer.module.values.get(result.left).isComptime() and analyzer.module.values.get(result.right).isComptime()) { + unreachable; + } else { + const assignment_index = try analyzer.module.assignments.append(analyzer.allocator, .{ + .store = result.left, + .load = result.right, + }); + return assignment_index; + } + } + + fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index { + var reaches_end = true; + const block_node = analyzer.nodes[node_index.unwrap()]; + var statement_nodes = ArrayList(Node.Index){}; + switch (block_node.id) { + .block_one, .comptime_block_one => { + try statement_nodes.append(analyzer.allocator, block_node.left); + }, + .block_zero, .comptime_block_zero => {}, else => |t| @panic(@tagName(t)), } - var statement_values = ArrayList(Value.Index){}; + const is_comptime = switch (block_node.id) { + .comptime_block_zero, .comptime_block_one => true, + .block_zero, .block_one => false, + else => |t| @panic(@tagName(t)), + }; + _ = is_comptime; + + var statements = ArrayList(Value.Index){}; + + for (statement_nodes.items) |statement_node_index| { + if (!reaches_end) { + unreachable; + } - for (statement_node_indices.items) |statement_node_index| { const statement_node = analyzer.nodes[statement_node_index.unwrap()]; - switch (statement_node.id) { - .assign => { - const assign_expression = try analyzer.assign(scope, statement_node_index); - try statement_values.append(analyzer.allocator, assign_expression); + const statement_value = switch (statement_node.id) { + inline .assign, .simple_while => |statement_id| blk: { + const specific_value_index = switch (statement_id) { + .assign => try analyzer.assign(scope, statement_node_index), + .simple_while => statement: { + const loop_index = 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; + + reaches_end = loop_structure.breaks or while_condition.valid; + + break :statement loop_index; + }, + else => unreachable, + }; + const value = @unionInit(Value, switch (statement_id) { + .assign => "assign", + .simple_while => "loop", + else => unreachable, + }, specific_value_index); + const value_index = try analyzer.module.values.append(analyzer.allocator, value); + break :blk value_index; }, else => |t| @panic(@tagName(t)), - } + }; + try statements.append(analyzer.allocator, statement_value); } - // TODO - - return Value.Index.invalid; + return try analyzer.module.blocks.append(analyzer.allocator, .{ + .statements = statements, + .reaches_end = reaches_end, + }); } - fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index { - const node = analyzer.nodes[node_index.unwrap()]; - - print("\nAssign. Left: {}. Right: {}\n", .{ node.left, node.right }); - // In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result``` - if (node.left.valid) { - @panic("Not discard"); - } else { - return try analyzer.expression(scope, ExpectType{ .none = {} }, node.right); - } + fn whileExpression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Loop.Index { + _ = node; + _ = expect_type; + _ = scope; + _ = analyzer; } - fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) !Block.Index { - const block_node = analyzer.nodes[node_index.unwrap()]; - var statements = ArrayList(Node.Index){}; - switch (block_node.id) { - .block_one => { - try statements.append(analyzer.allocator, block_node.left); - }, - .block_zero => {}, + 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)), - } - - for (statements.items) |statement_node_index| { - _ = try analyzer.expression(scope, expect_type, statement_node_index); - // const statement_node = analyzer.nodes[statement_node_index.unwrap()]; - // - // switch (statement_node.id) { - // try .simple_while => { - // const while_condition = try analyzer.expression(scope, ExpectType.boolean, statement_node.left); - // _ = while_condition; - // const while_block = try analyzer.block(scope, expect_type, statement_node.right); - // _ = while_block; - // unreachable; - // }, - // else => |t| @panic(@tagName(t)), - // } - } - - return try analyzer.module.blocks.append(analyzer.allocator, .{}); + }; + value.* = try analyzer.resolveNode(scope, expect_type, node_index); } - fn expression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) error{OutOfMemory}!Value.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); + // 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") { + return declaration.init_value; + } else { + unreachable; + } + } else { + @panic("TODO: not found"); + } + } + + 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 => blk: { - const identifier_hash = try analyzer.identifierFromToken(node.token); - // 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); - break :blk try analyzer.analyzeDeclaration(scope, declaration); - } else { - @panic("TODO: not found"); - } - }, + .identifier => unreachable, .compiler_intrinsic_one => blk: { const intrinsic_name = analyzer.tokenIdentifier(node.token + 1); const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable; @@ -148,14 +217,9 @@ const Analyzer = struct { unreachable; } - const file_struct_declaration_index = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file); - break :blk try analyzer.module.values.append(analyzer.allocator, .{ - .type = .{ - .declaration = file_struct_declaration_index, - }, - .is_const = true, - .is_comptime = true, - }); + break :blk .{ + .type = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file), + }; }, else => unreachable, } @@ -174,15 +238,27 @@ const Analyzer = struct { .prototype = function_prototype_index, .body = function_body, }); - const value_index = try analyzer.module.values.append(analyzer.allocator, .{ - .type = .{ - .function = function_index, - }, - .is_const = true, - .is_comptime = true, - }); - break :blk value_index; + break :blk .{ + .function = function_index, + }; }, + .keyword_true => unreachable, + .simple_while => unreachable, + // .assign => try analyzer.assign(scope, node_index), + .block_zero, .block_one => blk: { + const block_index = try analyzer.block(scope, expect_type, node_index); + break :blk .{ + .block = block_index, + }; + }, + 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: { switch (expect_type) { .none => {}, @@ -195,22 +271,9 @@ const Analyzer = struct { break :blk bool_true; }, - .simple_while => blk: { - const while_condition = try analyzer.expression(scope, ExpectType.boolean, node.left); - _ = while_condition; - const while_body = try analyzer.block(scope, expect_type, node.right); - _ = while_body; - const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{}); - const value_index = try analyzer.module.values.append(analyzer.allocator, .{ - .type = .{ - .loop = loop_index, - }, - // TODO: - .is_const = false, - .is_comptime = false, - }); - break :blk value_index; - }, + .block_zero => try analyzer.module.values.append(analyzer.allocator, .{ + .block = try analyzer.block(scope, expect_type, node_index), + }), else => |t| @panic(@tagName(t)), }; } @@ -249,44 +312,36 @@ const Analyzer = struct { } fn analyzeDeclaration(analyzer: *Analyzer, scope: *Scope, declaration: *Declaration) !Value.Index { - switch (declaration.*) { - .unresolved => |node_index| { - const declaration_node = analyzer.nodes[node_index.unwrap()]; - return switch (declaration_node.id) { - .simple_variable_declaration => blk: { - const expect_type = switch (declaration_node.left.valid) { - true => unreachable, - false => @unionInit(ExpectType, "none", {}), - }; - - const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right); - const value = analyzer.module.values.get(initialization_expression); - if (value.is_comptime and value.is_const) { - break :blk initialization_expression; - } - - unreachable; - }, - else => |t| @panic(@tagName(t)), - }; - }, - .struct_type => unreachable, - } + _ = declaration; + _ = scope; + _ = analyzer; + // switch (declaration.*) { + // .unresolved => |node_index| { + // const declaration_node = analyzer.nodes[node_index.unwrap()]; + // return switch (declaration_node.id) { + // .simple_variable_declaration => blk: { + // const expect_type = switch (declaration_node.left.valid) { + // true => unreachable, + // false => @unionInit(ExpectType, "none", {}), + // }; + // + // const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right); + // const value = analyzer.module.values.get(initialization_expression); + // if (value.is_comptime and value.is_const) { + // break :blk initialization_expression; + // } + // + // unreachable; + // }, + // else => |t| @panic(@tagName(t)), + // }; + // }, + // .struct_type => unreachable, + // } @panic("TODO: analyzeDeclaration"); } - fn containerMember(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !void { - const node = analyzer.nodes[node_index.unwrap()]; - switch (node.id) { - .simple_variable_declaration => {}, - .@"comptime" => { - _ = try analyzer.comptimeBlock(scope, node_index); - }, - else => std.debug.panic("Tag: {}", .{node.id}), - } - } - fn globalSymbolDeclaration(analyzer: *Analyzer, symbol_declaration: SymbolDeclaration) !void { if (symbol_declaration.type_node.get()) |type_node_index| { _ = type_node_index; @@ -339,21 +394,24 @@ const Analyzer = struct { }; } - fn structDeclaration(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Declaration.Index { + 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_scope, Type.Index.invalid); + const new_scope = try analyzer.allocateScope(.{ .parent = parent_scope }); const scope = new_scope.ptr; const is_file = !parent_scope.valid; assert(is_file); - // TODO: do it properly - const declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{ - .struct_type = .{ - .scope = new_scope.index, - .initialization = if (is_file) Value.Index.invalid else unreachable, - }, + + const struct_index = try analyzer.module.structs.append(analyzer.allocator, .{ + .scope = new_scope.index, }); - // TODO: + 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: { @@ -391,6 +449,11 @@ const Analyzer = struct { 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) { @@ -416,7 +479,14 @@ const Analyzer = struct { } const container_declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{ - .unresolved = declaration_node_index, + .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; @@ -429,8 +499,9 @@ const Analyzer = struct { for (declaration_nodes.items) |declaration_node_index| { const declaration_node = analyzer.nodes[declaration_node_index.unwrap()]; switch (declaration_node.id) { - .@"comptime", .simple_variable_declaration => try analyzer.containerMember(scope, declaration_node_index), - else => unreachable, + .@"comptime" => _ = try analyzer.comptimeBlock(scope, declaration_node_index), + .simple_variable_declaration => {}, + else => |t| @panic(@tagName(t)), } } @@ -441,7 +512,7 @@ const Analyzer = struct { @panic("TODO: fields"); } - return declaration_index; + return type_index; } const MemberType = enum { @@ -494,11 +565,8 @@ const Analyzer = struct { index: Scope.Index, }; - fn allocateScope(analyzer: *Analyzer, parent_scope: Scope.Index, scope_type: Type.Index) !ScopeAllocation { - const scope_index = try analyzer.module.scopes.append(analyzer.allocator, .{ - .parent = parent_scope, - .type = scope_type, - }); + 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 .{ @@ -512,6 +580,9 @@ const ExpectType = union(enum) { none, type_index: Type.Index, + pub const none = ExpectType{ + .none = {}, + }; pub const boolean = ExpectType{ .type_index = type_boolean, }; @@ -562,7 +633,7 @@ const HardwareSignedIntegerType = enum { const offset = HardwareUnsignedIntegerType.offset + @typeInfo(HardwareUnsignedIntegerType).Enum.fields.len; }; -pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Declaration.Index { +pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Type.Index { inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { _ = try module.types.append(compilation.base_allocator, @unionInit(Type, enum_field.name, {})); } @@ -596,25 +667,17 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) } _ = try module.values.append(compilation.base_allocator, .{ - .type = .{ - .bool_false = {}, - }, - .is_const = true, - .is_comptime = true, + .bool = false, }); _ = try module.values.append(compilation.base_allocator, .{ - .type = .{ - .bool_true = {}, - }, - .is_const = true, - .is_comptime = true, + .bool = true, }); return analyzeExistingPackage(compilation, module, package); } -pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, package: *Package) !Declaration.Index { +pub fn analyzeExistingPackage(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; @@ -622,7 +685,7 @@ pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, packag return try analyzeFile(compilation.base_allocator, module, package_file); } -pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Declaration.Index { +pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Type.Index { assert(file.status == .parsed); var analyzer = Analyzer{ @@ -634,7 +697,7 @@ pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Declarat .module = module, }; - const result = try analyzer.structDeclaration(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 }); + const result = try analyzer.structType(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 }); return result; } diff --git a/src/frontend/syntactic_analyzer.zig b/src/frontend/syntactic_analyzer.zig index 8bfbe81..36f7fcd 100644 --- a/src/frontend/syntactic_analyzer.zig +++ b/src/frontend/syntactic_analyzer.zig @@ -17,6 +17,10 @@ pub const Result = struct { time: u64, }; +pub const Options = packed struct { + is_comptime: bool, +}; + // TODO: pack it to be more efficient pub const Node = packed struct(u128) { token: u32, @@ -68,6 +72,8 @@ pub const Node = packed struct(u128) { function_definition = 16, keyword_noreturn = 17, keyword_true = 18, + comptime_block_zero = 19, + comptime_block_one = 20, }; }; @@ -111,7 +117,7 @@ const Analyzer = struct { .fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) { .left_brace => blk: { analyzer.token_i += 1; - const comptime_block = try analyzer.block(); + const comptime_block = try analyzer.block(.{ .is_comptime = true }); break :blk .{ .id = .@"comptime", @@ -181,7 +187,9 @@ const Analyzer = struct { fn function(analyzer: *Analyzer) !Node.Index { const token = analyzer.token_i; const function_prototype = try analyzer.functionPrototype(); - const function_body = try analyzer.block(); + const is_comptime = false; + _ = is_comptime; + const function_body = try analyzer.block(.{ .is_comptime = false }); return analyzer.addNode(.{ .id = .function_definition, .token = token, @@ -223,27 +231,50 @@ const Analyzer = struct { } } - fn block(analyzer: *Analyzer) !Node.Index { + fn block(analyzer: *Analyzer, options: Options) !Node.Index { const left_brace = try analyzer.expectToken(.left_brace); const node_heap_top = analyzer.temporal_node_heap.items.len; defer analyzer.temporal_node_heap.shrinkRetainingCapacity(node_heap_top); while (analyzer.tokens[analyzer.token_i].id != .right_brace) { - const statement_index = try analyzer.statement(); + const first_statement_token = analyzer.tokens[analyzer.token_i]; + const statement_index = switch (first_statement_token.id) { + .identifier => switch (analyzer.tokens[analyzer.token_i + 1].id) { + .colon => { + unreachable; + }, + else => blk: { + const identifier = analyzer.getIdentifier(first_statement_token); + std.debug.print("Starting statement with identifier: {s}\n", .{identifier}); + const result = try analyzer.assignExpression(); + _ = try analyzer.expectToken(.semicolon); + break :blk result; + }, + }, + .fixed_keyword_while => try analyzer.whileStatement(options), + else => unreachable, + }; try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index); } + _ = try analyzer.expectToken(.right_brace); const statement_array = analyzer.temporal_node_heap.items[node_heap_top..]; const node: Node = switch (statement_array.len) { 0 => .{ - .id = .block_zero, + .id = switch (options.is_comptime) { + true => .comptime_block_zero, + false => .block_zero, + }, .token = left_brace, .left = Node.Index.invalid, .right = Node.Index.invalid, }, 1 => .{ - .id = .block_one, + .id = switch (options.is_comptime) { + true => .comptime_block_one, + false => .block_one, + }, .token = left_brace, .left = statement_array[0], .right = Node.Index.invalid, @@ -253,28 +284,7 @@ const Analyzer = struct { return analyzer.addNode(node); } - fn statement(analyzer: *Analyzer) !Node.Index { - // TODO: more stuff before - const first_statement_token = analyzer.tokens[analyzer.token_i]; - return switch (first_statement_token.id) { - .identifier => switch (analyzer.tokens[analyzer.token_i + 1].id) { - .colon => { - unreachable; - }, - else => blk: { - const identifier = analyzer.getIdentifier(first_statement_token); - std.debug.print("Starting statement with identifier: {s}\n", .{identifier}); - const result = try analyzer.assignExpression(); - _ = try analyzer.expectToken(.semicolon); - break :blk result; - }, - }, - .fixed_keyword_while => try analyzer.whileStatement(), - else => unreachable, - }; - } - - fn whileStatement(analyzer: *Analyzer) error{ OutOfMemory, unexpected_token, not_implemented }!Node.Index { + fn whileStatement(analyzer: *Analyzer, options: Options) error{ OutOfMemory, unexpected_token, not_implemented }!Node.Index { const while_identifier_index = try analyzer.expectToken(.fixed_keyword_while); _ = try analyzer.expectToken(.left_parenthesis); @@ -282,7 +292,7 @@ const Analyzer = struct { const while_condition = try analyzer.expression(); _ = try analyzer.expectToken(.right_parenthesis); - const while_block = try analyzer.block(); + const while_block = try analyzer.block(options); return analyzer.addNode(.{ .id = .simple_while, @@ -300,7 +310,7 @@ const Analyzer = struct { else => unreachable, }; - return analyzer.addNode(.{ + const node = Node{ .id = expression_id, .token = blk: { const token_i = analyzer.token_i; @@ -309,7 +319,9 @@ const Analyzer = struct { }, .left = expr, .right = try analyzer.expression(), - }); + }; + std.debug.print("assign:\nleft: {}.\nright: {}\n", .{ node.left, node.right }); + return analyzer.addNode(node); } fn compilerIntrinsic(analyzer: *Analyzer) !Node.Index { @@ -405,7 +417,8 @@ const Analyzer = struct { else => try analyzer.curlySuffixExpression(), }, .string_literal, .fixed_keyword_true, .fixed_keyword_false => try analyzer.curlySuffixExpression(), - .left_brace => try analyzer.block(), + // todo:? + // .left_brace => try analyzer.block(), else => |id| { log.warn("By default, calling curlySuffixExpression with {s}", .{@tagName(id)}); unreachable; @@ -519,6 +532,7 @@ const Analyzer = struct { .colon => unreachable, else => blk: { const identifier = analyzer.getIdentifier(token); + std.debug.print("identifier: {s}\n", .{identifier}); analyzer.token_i += 1; if (equal(u8, identifier, "_")) { break :blk Node.Index.invalid; @@ -549,10 +563,9 @@ const Analyzer = struct { } fn addNode(analyzer: *Analyzer, node: Node) !Node.Index { - std.debug.print("Adding node {s}\n", .{@tagName(node.id)}); const index = analyzer.nodes.items.len; try analyzer.nodes.append(analyzer.allocator, node); - + std.debug.print("Adding node #{} {s}\n", .{ index, @tagName(node.id) }); return Node.Index{ .value = @intCast(index), };