From 8a042c58e5ad34a39d4189822e7b7ade559d0195 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 8 Dec 2023 23:17:45 +0100 Subject: [PATCH] implement basic builder --- bootstrap/Compilation.zig | 414 +++-- bootstrap/backend/c_transpiler.zig | 918 +++++++--- bootstrap/data_structures.zig | 10 +- bootstrap/frontend/lexical_analyzer.zig | 5 +- bootstrap/frontend/semantic_analyzer.zig | 1923 ++++++++++++++------- bootstrap/frontend/syntactic_analyzer.zig | 441 +++-- ci.sh | 3 +- lib/std/build.nat | 42 +- lib/std/os.nat | 284 ++- lib/std/os/linux.nat | 73 + lib/std/start.nat | 14 +- test/div/main.nat | 2 +- test/foreach/main.nat | 2 +- test/fork/main.nat | 1 + test/fork_exec/main.nat | 6 +- test/imul/main.nat | 2 +- test/loop_break/main.nat | 11 + test/self_exe_path/main.nat | 5 +- test/simple_bool/main.nat | 2 +- test/virtual_memory/main.nat | 1 + 20 files changed, 2852 insertions(+), 1307 deletions(-) create mode 100644 test/loop_break/main.nat diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index c98592a..a01f286 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -54,6 +54,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { var maybe_main_package_path: ?[]const u8 = null; var target_triplet: []const u8 = "x86_64-linux-gnu"; var should_transpile_to_c: ?bool = null; + var maybe_only_parse: ?bool = null; var i: usize = 0; while (i < arguments.len) : (i += 1) { @@ -128,6 +129,16 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { } else { reportUnterminatedArgumentError(current_argument); } + } else if (equal(u8, current_argument, "-parse")) { + if (i + 1 != arguments.len) { + i += 1; + + const arg = arguments[i]; + maybe_main_package_path = arg; + maybe_only_parse = true; + } else { + reportUnterminatedArgumentError(current_argument); + } } else { maybe_main_package_path = current_argument; } @@ -136,6 +147,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { const cross_target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = target_triplet }); const target = cross_target.toTarget(); const transpile_to_c = should_transpile_to_c orelse true; + const only_parse = maybe_only_parse orelse false; var is_build = false; const main_package_path = if (maybe_main_package_path) |path| path else blk: { @@ -160,6 +172,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { .target = target, .transpile_to_c = transpile_to_c, .is_build = is_build, + .only_parse = only_parse, }; } @@ -211,24 +224,6 @@ pub const ContainerInitialization = struct { pub const Index = List.Index; }; -pub const Enum = struct { - scope: Scope.Index, - fields: ArrayList(Enum.Field.Index) = .{}, - backing_type: Type.Index, - - pub const Field = struct { - name: u32, - value: Value.Index, - parent: Type.Index, - - pub const List = BlockList(@This()); - pub const Index = Enum.Field.List.Index; - }; - - pub const List = BlockList(@This()); - pub const Index = List.Index; -}; - pub const Type = union(enum) { any, void, @@ -277,6 +272,24 @@ pub const Type = union(enum) { pub const List = BlockList(@This()); pub const Index = List.Index; + pub const Enum = struct { + scope: Scope.Index, + fields: ArrayList(Enum.Field.Index) = .{}, + backing_type: Type.Index, + + pub const Field = struct { + name: u32, + value: Value.Index, + parent: Type.Index, + + pub const List = BlockList(@This()); + pub const Index = Enum.Field.List.Index; + }; + + pub const List = BlockList(@This()); + pub const Index = Enum.List.Index; + }; + pub const Integer = struct { bit_count: u16, signedness: Signedness, @@ -365,13 +378,40 @@ pub const Type = union(enum) { }); }; +pub const Intrinsic = struct { + kind: Kind, + type: Type.Index, + + pub const Kind = union(enum) { + cast: Value.Index, + array_coerce_to_slice: Value.Index, + min: Binary, + optional_wrap: Value.Index, + optional_unwrap: Value.Index, + optional_check: Value.Index, + sign_extend: Value.Index, + syscall: ArrayList(Value.Index), + zero_extend: Value.Index, + + pub const Binary = struct { + left: Value.Index, + right: Value.Index, + }; + }; + + pub const List = BlockList(@This()); + pub const Index = @This().List.Index; +}; + // Each time an enum is added here, a corresponding insertion in the initialization must be made -pub const Intrinsic = enum { +pub const ValidIntrinsic = enum { //@"asm", this is processed separately as it need special parsing + cast, @"error", import, + min, + size, syscall, - cast, }; pub const FixedTypeKeyword = enum { @@ -460,14 +500,36 @@ pub const Declaration = struct { init_value: Value.Index, name: u32, argument_index: ?u32, - type: Type.Index, + // A union is needed here because of global lazy declarations + type: Declaration.Type, scope: Scope.Index, pub const Reference = struct { value: Declaration.Index, - type: Type.Index, + + pub fn getType(reference: Reference, module: *Module) Compilation.Type.Index { + return module.values.declarations.get(reference.value).getType(); + } }; + pub const Type = union(enum) { + resolved: Compilation.Type.Index, + inferred: Compilation.Type.Index, + unresolved: Node.Index, + }; + + pub fn getType(declaration: *Declaration) Compilation.Type.Index { + const type_index: Compilation.Type.Index = switch (declaration.type) { + .resolved => |resolved| resolved, + .inferred => |inferred| inferred, + .unresolved => unreachable, + }; + + assert(!type_index.invalid); + + return type_index; + } + pub const List = BlockList(@This()); pub const Index = List.Index; }; @@ -478,7 +540,7 @@ pub const Function = struct { prototype: Type.Index, pub const Prototype = struct { - arguments: ?[]const Declaration.Index, + arguments: ArrayList(Declaration.Index), return_type: Type.Index, attributes: Attributes = .{}, @@ -610,6 +672,7 @@ pub const BinaryOperation = struct { compare_greater_or_equal, compare_less_than, compare_less_or_equal, + compare_not_equal, }; }; @@ -644,6 +707,19 @@ pub const Branch = struct { pub const Index = List.Index; }; +pub const Switch = struct { + value: Value.Index, + groups: ArrayList(Group), + + pub const Group = struct { + conditions: ArrayList(Value.Index), + expression: Value.Index, + }; + + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + pub const FieldAccess = struct { declaration_reference: Value.Index, field: ContainerField.Index, @@ -728,12 +804,14 @@ pub const Assembly = struct { pub const Instruction = enum { @"and", call, + mov, xor, }; pub const Register = enum { ebp, rsp, + rdi, }; }; }; @@ -745,12 +823,14 @@ pub const StringLiteral = struct { pub const Value = union(enum) { void, - bool: bool, + @"break", undefined, @"unreachable", + bool: bool, pointer_null_literal, optional_null_literal, unresolved: Unresolved, + intrinsic: Intrinsic.Index, declaration: Declaration.Index, declaration_reference: Declaration.Reference, loop: Loop.Index, @@ -760,20 +840,16 @@ pub const Value = union(enum) { assign: Assignment.Index, type: Type.Index, integer: Integer, - syscall: Syscall.Index, call: Call.Index, argument_list: ArgumentList, @"return": Return.Index, argument: Declaration.Index, string_literal: StringLiteral, - enum_field: Enum.Field.Index, + enum_field: Type.Enum.Field.Index, extern_function: Function.Prototype.Index, - sign_extend: Cast.Index, - zero_extend: Cast.Index, binary_operation: BinaryOperation.Index, unary_operation: UnaryOperation.Index, branch: Branch.Index, - cast: Cast.Index, container_initialization: ContainerInitialization.Index, array_initialization: ContainerInitialization.Index, field_access: FieldAccess.Index, @@ -781,10 +857,11 @@ pub const Value = union(enum) { indexed_access: IndexedAccess.Index, optional_check: OptionalCheck.Index, optional_unwrap: OptionalUnwrap.Index, - optional_cast: Cast.Index, + // optional_cast: Cast.Index, array_coerce_to_slice: Cast.Index, slice: Slice.Index, assembly_block: Assembly.Block.Index, + switch_expression: Switch.Index, pub const List = BlockList(@This()); pub const Index = List.Index; @@ -800,20 +877,26 @@ pub const Value = union(enum) { }; pub fn isComptime(value: *Value, module: *Module) bool { + _ = module; + return switch (value.*) { .integer => |integer| integer.type.eq(Type.comptime_int), - .declaration_reference => |declaration_reference| module.values.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.array.get(module.values.declarations.get(declaration_reference.value).init_value), module), - .bool, .void, .undefined, .function_definition, .type, .enum_field => true, + .declaration_reference => false, + .bool, .void, .function_definition, .type, .enum_field => true, // TODO: .call, - .syscall, + // .syscall, .binary_operation, .container_initialization, - .cast, + // .cast, .optional_unwrap, .pointer_null_literal, .indexed_access, .slice, + .array_initialization, + .undefined, + .intrinsic, + .field_access, => false, // TODO: else => |t| @panic(@tagName(t)), @@ -824,7 +907,7 @@ pub const Value = union(enum) { const result = switch (value) { .call => |call_index| module.values.calls.get(call_index).type, .integer => |integer| integer.type, - .declaration_reference => |declaration_reference| declaration_reference.type, + .declaration_reference => |declaration_reference| declaration_reference.getType(module), .string_literal => |string_literal| string_literal.type, .type => Type.type, .enum_field => |enum_field_index| module.types.enum_fields.get(enum_field_index).parent, @@ -832,19 +915,18 @@ pub const Value = union(enum) { .function_declaration => |function_index| module.types.function_declarations.get(function_index).prototype, .binary_operation => |binary_operation| module.values.binary_operations.get(binary_operation).type, .bool => Type.boolean, - .declaration => Type.void, + // .declaration => Type.void, .container_initialization, .array_initialization, => |initialization| module.values.container_initializations.get(initialization).type, - .syscall => Type.usize, + .intrinsic => |intrinsic_index| module.values.intrinsics.get(intrinsic_index).type, .unary_operation => |unary_operation_index| module.values.unary_operations.get(unary_operation_index).type, .pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type, .optional_null_literal => semantic_analyzer.optional_any, .field_access => |field_access_index| module.types.container_fields.get(module.values.field_accesses.get(field_access_index).field).type, - .cast, - .optional_cast, - .array_coerce_to_slice, - => |cast_index| module.values.casts.get(cast_index).type, + // .cast, + // .optional_cast, + // .array_coerce_to_slice => |cast_index| module.values.casts.get(cast_index).type, .slice => |slice_index| module.values.slices.get(slice_index).type, .slice_access => |slice_access_index| module.values.slice_accesses.get(slice_access_index).type, .optional_check => Type.boolean, @@ -854,6 +936,22 @@ pub const Value = union(enum) { const indexed_expression_type = module.types.array.get(indexed_expression_type_index); break :blk switch (indexed_expression_type.*) { .slice => |slice| slice.element_type, + .array => |array| array.element_type, + .pointer => |pointer| switch (pointer.many) { + true => pointer.element_type, + false => unreachable, + }, + else => |t| @panic(@tagName(t)), + }; + }, + .undefined => Type.any, + .optional_unwrap => |optional_unwrap_index| blk: { + const optional_unwrap = module.values.optional_unwraps.get(optional_unwrap_index); + const optional_value = module.values.array.get(optional_unwrap.value); + const expected_optional_type_index = optional_value.getType(module); + const expected_optional_type = module.types.array.get(expected_optional_type_index); + break :blk switch (expected_optional_type.*) { + .optional => |optional| optional.element_type, else => |t| @panic(@tagName(t)), }; }, @@ -880,12 +978,13 @@ pub const Module = struct { blocks: BlockList(Block) = .{}, loops: BlockList(Loop) = .{}, assignments: BlockList(Assignment) = .{}, - syscalls: BlockList(Syscall) = .{}, + intrinsics: BlockList(Intrinsic) = .{}, + // syscalls: BlockList(Syscall) = .{}, calls: BlockList(Call) = .{}, argument_lists: BlockList(ArgumentList) = .{}, returns: BlockList(Return) = .{}, container_initializations: BlockList(ContainerInitialization) = .{}, - casts: BlockList(Cast) = .{}, + // casts: BlockList(Cast) = .{}, branches: BlockList(Branch) = .{}, binary_operations: BlockList(BinaryOperation) = .{}, unary_operations: BlockList(UnaryOperation) = .{}, @@ -896,13 +995,14 @@ pub const Module = struct { optional_unwraps: BlockList(OptionalUnwrap) = .{}, assembly_blocks: BlockList(Assembly.Block) = .{}, assembly_instructions: BlockList(Assembly.Instruction) = .{}, + switches: BlockList(Switch) = .{}, } = .{}, types: struct { array: BlockList(Type) = .{}, - enums: BlockList(Enum) = .{}, + enums: BlockList(Type.Enum) = .{}, structs: BlockList(Struct) = .{}, container_fields: BlockList(ContainerField) = .{}, - enum_fields: BlockList(Enum.Field) = .{}, + enum_fields: BlockList(Type.Enum.Field) = .{}, function_definitions: BlockList(Function) = .{}, function_declarations: BlockList(Function) = .{}, function_prototypes: BlockList(Function.Prototype) = .{}, @@ -928,6 +1028,7 @@ pub const Module = struct { target: std.Target, transpile_to_c: bool, is_build: bool, + only_parse: bool, }; const ImportFileResult = struct { @@ -1144,109 +1245,132 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! assert(module.main_package.dependencies.size == 2); - _ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?); + if (!descriptor.only_parse) { + _ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?); + } else { + _ = try module.importPackage(compilation.base_allocator, module.main_package); + } for (module.map.imports.values()) |import| { try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import); } - inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { - _ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { - .usize => @unionInit(Type, "integer", .{ - .bit_count = 64, - .signedness = .unsigned, - }), - .ssize => @unionInit(Type, "integer", .{ - .bit_count = 64, - .signedness = .signed, - }), - else => @unionInit(Type, enum_field.name, {}), - }); - } - - inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| { - _ = try module.types.array.append(compilation.base_allocator, .{ - .integer = .{ - .signedness = .unsigned, - .bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) { - .u8 => 8, - .u16 => 16, - .u32 => 32, - .u64 => 64, - }, - }, - }); - } - - inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| { - _ = try module.types.array.append(compilation.base_allocator, .{ - .integer = .{ - .signedness = .signed, - .bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) { - .s8 => 8, - .s16 => 16, - .s32 => 32, - .s64 => 64, - }, - }, - }); - } - - for (extra_common_type_data) |type_data| { - _ = try module.types.array.append(compilation.base_allocator, type_data); - } - semantic_analyzer.pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ - .pointer = .{ - .element_type = Type.any, - .many = false, - .@"const" = true, - .termination = .none, - }, - }); - semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ - .optional = .{ - .element_type = semantic_analyzer.pointer_to_any_type, - }, - }); - semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{ - .optional = .{ - .element_type = Type.any, - }, - }); - - semantic_analyzer.unreachable_index = try module.values.array.append(compilation.base_allocator, .@"unreachable"); - semantic_analyzer.pointer_null_index = try module.values.array.append(compilation.base_allocator, .pointer_null_literal); - semantic_analyzer.optional_null_index = try module.values.array.append(compilation.base_allocator, .optional_null_literal); - semantic_analyzer.undefined_index = try module.values.array.append(compilation.base_allocator, .undefined); - - const value_index = try module.values.array.append(compilation.base_allocator, .{ - .unresolved = .{ - .node_index = .{ .value = 0 }, - }, - }); - - try semantic_analyzer.initialize(compilation, module, packages[0], value_index); - - if (descriptor.transpile_to_c) { - try c_transpiler.initialize(compilation, module, descriptor); - if (descriptor.is_build) { - var process = std.ChildProcess.init(&.{descriptor.executable_path}, compilation.base_allocator); - switch (try process.spawnAndWait()) { - .Exited => |exit_code| { - if (exit_code != 0) { - @panic("Exited with errors"); - } - }, - else => @panic("Unexpected program state"), - } + if (!descriptor.only_parse) { + inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { + _ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { + .usize => @unionInit(Type, "integer", .{ + .bit_count = 64, + .signedness = .unsigned, + }), + .ssize => @unionInit(Type, "integer", .{ + .bit_count = 64, + .signedness = .signed, + }), + else => @unionInit(Type, enum_field.name, {}), + }); + } + + inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| { + _ = try module.types.array.append(compilation.base_allocator, .{ + .integer = .{ + .signedness = .unsigned, + .bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) { + .u8 => 8, + .u16 => 16, + .u32 => 32, + .u64 => 64, + }, + }, + }); + } + + inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| { + _ = try module.types.array.append(compilation.base_allocator, .{ + .integer = .{ + .signedness = .signed, + .bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) { + .s8 => 8, + .s16 => 16, + .s32 => 32, + .s64 => 64, + }, + }, + }); + } + + for (extra_common_type_data) |type_data| { + _ = try module.types.array.append(compilation.base_allocator, type_data); + } + semantic_analyzer.pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ + .pointer = .{ + .element_type = Type.any, + .many = false, + .@"const" = true, + .termination = .none, + }, + }); + semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ + .optional = .{ + .element_type = semantic_analyzer.pointer_to_any_type, + }, + }); + semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{ + .optional = .{ + .element_type = Type.any, + }, + }); + + semantic_analyzer.unreachable_index = try module.values.array.append(compilation.base_allocator, .@"unreachable"); + semantic_analyzer.pointer_null_index = try module.values.array.append(compilation.base_allocator, .pointer_null_literal); + semantic_analyzer.optional_null_index = try module.values.array.append(compilation.base_allocator, .optional_null_literal); + semantic_analyzer.undefined_index = try module.values.array.append(compilation.base_allocator, .undefined); + semantic_analyzer.boolean_false = try module.values.array.append(compilation.base_allocator, .{ + .bool = false, + }); + semantic_analyzer.boolean_true = try module.values.array.append(compilation.base_allocator, .{ + .bool = true, + }); + + const value_index = try module.values.array.append(compilation.base_allocator, .{ + .unresolved = .{ + .node_index = .{ .value = 0 }, + }, + }); + + try semantic_analyzer.initialize(compilation, module, packages[0], value_index); + + if (descriptor.transpile_to_c) { + try c_transpiler.initialize(compilation, module, descriptor); + if (descriptor.is_build) { + const argv = [_][]const u8{ descriptor.executable_path, "--compiler", compilation.executable_absolute_path }; + const process_result = try std.ChildProcess.run(.{ + .allocator = compilation.base_allocator, + .argv = &argv, + }); + + switch (process_result.term) { + .Exited => |exit_code| { + if (exit_code != 0) { + for (argv) |arg| { + std.debug.print("{s} ", .{arg}); + } + std.debug.print("exited with failure: {}\n", .{exit_code}); + std.debug.print("STDOUT:\n```\n{s}\n```\n", .{process_result.stdout}); + std.debug.print("STDERR:\n```\n{s}\n```\n", .{process_result.stderr}); + @panic("Internal error"); + } + }, + else => @panic("Unexpected program state"), + } + } + } else { + unreachable; + // const ir = try intermediate_representation.initialize(compilation, module); + // + // switch (descriptor.target.cpu.arch) { + // inline else => |arch| try emit.get(arch).initialize(compilation.base_allocator, ir, descriptor), + // } } - } else { - unreachable; - // const ir = try intermediate_representation.initialize(compilation, module); - // - // switch (descriptor.target.cpu.arch) { - // inline else => |arch| try emit.get(arch).initialize(compilation.base_allocator, ir, descriptor), - // } } } @@ -1317,6 +1441,10 @@ pub const File = struct { fn parse(file: *File, allocator: Allocator, file_index: File.Index) !void { assert(file.status == .lexed); file.syntactic_analyzer_result = try syntactic_analyzer.analyze(allocator, file.lexical_analyzer_result.tokens.items, file.source_code, file_index); + + // if (file_index.uniqueInteger() == 5) { + // assert(file.syntactic_analyzer_result.nodes.items[1356].id != .node_list); + // } // if (!@import("builtin").is_test) { // print("[SYNTACTIC ANALYSIS] {} ns\n", .{file.syntactic_analyzer_result.time}); // } @@ -1353,7 +1481,7 @@ fn getLoggerScopeType(comptime logger_scope: LoggerScope) type { var logger_bitset = std.EnumSet(LoggerScope).initEmpty(); -var writer = std.io.getStdErr().writer(); +var writer = std.io.getStdOut().writer(); fn shouldLog(comptime logger_scope: LoggerScope, logger: getLoggerScopeType(logger_scope).Logger) bool { return logger_bitset.contains(logger_scope) and getLoggerScopeType(logger_scope).Logger.bitset.contains(logger); diff --git a/bootstrap/backend/c_transpiler.zig b/bootstrap/backend/c_transpiler.zig index db06637..3763614 100644 --- a/bootstrap/backend/c_transpiler.zig +++ b/bootstrap/backend/c_transpiler.zig @@ -58,12 +58,14 @@ pub const TranslationUnit = struct { string_literals: ArrayList(u8) = .{}, primitive_type_declarations: ArrayList(u8) = .{}, type_forward_declarations: ArrayList(u8) = .{}, + macros: ArrayList(u8) = .{}, type_declarations: ArrayList(u8) = .{}, function_declarations: ArrayList(u8) = .{}, global_variable_declarations: ArrayList(u8) = .{}, function_definitions: ArrayList(u8) = .{}, syscall_bitset: SyscallBitset = SyscallBitset.initEmpty(), function_set: AutoArrayHashMap(Compilation.Function.Index, []const u8) = .{}, + macro_set: std.EnumSet(Macro) = std.EnumSet(Macro).initEmpty(), struct_type_set: TypeSet = .{}, optional_type_set: TypeSet = .{}, slice_type_set: TypeSet = .{}, @@ -141,7 +143,7 @@ pub const TranslationUnit = struct { // } if (declaration.mutability == .@"const") { - switch (module.types.array.get(declaration.type).*) { + switch (module.types.array.get(declaration.getType()).*) { .optional => |optional| switch (module.types.array.get(optional.element_type).*) { .pointer => {}, else => { @@ -162,7 +164,7 @@ pub const TranslationUnit = struct { } } - try unit.writeType(module, list, allocator, declaration.type, separation_character); + try unit.writeType(module, list, allocator, declaration.getType(), separation_character); try list.append(allocator, ' '); @@ -172,13 +174,13 @@ pub const TranslationUnit = struct { try unit.writeValue(module, list, allocator, Type.Index.invalid, indentation, .{ .value_index = declaration.init_value, - .type_index = declaration.type, + .type_index = declaration.getType(), }); } fn writeAssignment(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, assignment_index: Compilation.Assignment.Index, function_return_type: Type.Index, indentation: usize) !void { const assignment = module.values.assignments.get(assignment_index); - const left_type = module.values.array.get(assignment.source).getType(module); + const left_type = module.values.array.get(assignment.destination).getType(module); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = assignment.destination, .type_index = left_type, @@ -189,12 +191,50 @@ pub const TranslationUnit = struct { .add => try list.append(allocator, '+'), } try list.appendSlice(allocator, "= "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = assignment.source, - .type_index = Type.Index.invalid, + .type_index = left_type, }); } + fn writeReturn(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, return_index: Compilation.Return.Index, function_return_type: Type.Index, indentation: usize) !void { + const return_expr = module.values.returns.get(return_index); + try list.appendSlice(allocator, "return "); + const return_value = module.values.array.get(return_expr.value); + const return_value_type_index = return_value.getType(module); + // _ = return_value_type_index; + switch (module.types.array.get(function_return_type).*) { + .optional => switch (module.types.array.get(return_value_type_index).*) { + .optional => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = return_expr.value, + .type_index = function_return_type, + }), + else => { + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, function_return_type, '_'); + try list.appendSlice(allocator, ") {\n"); + + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".value = "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = return_expr.value, + .type_index = return_value_type_index, + }); + try list.appendSlice(allocator, ",\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".is_null = false,\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.append(allocator, '}'); + }, + }, + else => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = return_expr.value, + .type_index = function_return_type, + }), + } + } + fn writeBlock(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, block_index: Compilation.Block.Index, function_return_type: Type.Index, old_indentation: usize) !void { try list.appendSlice(allocator, "{\n"); const block = module.values.blocks.get(block_index); @@ -215,45 +255,7 @@ pub const TranslationUnit = struct { try list.append(allocator, ';'); }, .@"return" => |return_index| { - const return_expr = module.values.returns.get(return_index); - try list.appendSlice(allocator, "return "); - const return_value = module.values.array.get(return_expr.value); - const return_value_type_index = return_value.getType(module); - // _ = return_value_type_index; - switch (module.types.array.get(function_return_type).*) { - .optional => switch (module.types.array.get(return_value_type_index).*) { - .optional => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = return_expr.value, - .type_index = function_return_type, - }), - else => { - try list.append(allocator, '('); - try unit.writeType(module, list, allocator, function_return_type, '_'); - try list.appendSlice(allocator, ") {\n"); - - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.appendSlice(allocator, ".value = "); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = return_expr.value, - .type_index = return_value_type_index, - }); - try list.appendSlice(allocator, ",\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.appendSlice(allocator, ".is_null = false,\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.append(allocator, '}'); - }, - }, - else => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = return_expr.value, - .type_index = function_return_type, - }), - } - - try list.append(allocator, ';'); - }, - .syscall => |syscall_index| { - try unit.writeSyscall(module, list, allocator, syscall_index, function_return_type, indentation); + try unit.writeReturn(module, list, allocator, return_index, function_return_type, indentation); try list.append(allocator, ';'); }, .@"unreachable" => { @@ -265,33 +267,8 @@ pub const TranslationUnit = struct { try list.append(allocator, ';'); }, .branch => |branch_index| { - const branch = module.values.branches.get(branch_index); - try list.appendSlice(allocator, "if ("); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = branch.expression, - .type_index = Type.Index.invalid, - }); - try list.appendSlice(allocator, ") "); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = branch.taken_expression, - .type_index = function_return_type, - }); - - if (!branch.not_taken_expression.invalid) { - if (module.values.array.get(branch.taken_expression).* == .block) { - _ = list.pop(); - try list.appendSlice(allocator, " else "); - } else { - unreachable; - } - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = branch.not_taken_expression, - .type_index = function_return_type, - }); - - if (module.values.array.get(branch.not_taken_expression).* == .block) { - continue; - } + if (!try unit.writeBranch(module, list, allocator, branch_index, function_return_type, indentation)) { + continue; } }, .assembly_block => |assembly_block_index| { @@ -333,6 +310,61 @@ pub const TranslationUnit = struct { .block => |new_block_index| { try unit.writeBlock(module, list, allocator, new_block_index, function_return_type, indentation); }, + .switch_expression => |switch_index| { + const switch_expression = module.values.switches.get(switch_index); + try list.appendSlice(allocator, "switch ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = switch_expression.value, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ") {\n"); + + for (switch_expression.groups.items) |switch_case_group| { + for (switch_case_group.conditions.items) |condition_value_index| { + try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); + try list.appendSlice(allocator, "case "); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = condition_value_index, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ":\n"); + } + + _ = list.pop(); + + switch (module.values.array.get(switch_case_group.expression).*) { + .block => { + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = switch_case_group.expression, + .type_index = Type.Index.invalid, + }); + }, + else => { + try list.appendSlice(allocator, " {\n"); + + try unit.writeValue(module, list, allocator, function_return_type, indentation + 2, .{ + .value_index = switch_case_group.expression, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ";\n"); + + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, "}\n"); + }, + } + + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, " break;\n"); + } + + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, "};\n"); + }, + .intrinsic => |intrinsic_index| { + try unit.writeIntrinsic(module, list, allocator, intrinsic_index, function_return_type, indentation, .statement); + try list.append(allocator, ';'); + }, + .@"break" => try list.appendSlice(allocator, "break;"), else => |t| @panic(@tagName(t)), } @@ -371,7 +403,14 @@ pub const TranslationUnit = struct { return unit.declaration_set.values()[index]; } else { const declaration = module.values.declarations.get(declaration_index); - const base_declaration_name = module.getName(declaration.name).?; + const base_declaration_name = blk: { + const name = module.getName(declaration.name).?; + if (declaration.scope_type == .global and equal(u8, name, "main")) { + break :blk "user_entry_point"; + } else { + break :blk name; + } + }; var list = ArrayList(u8){}; try list.insertSlice(allocator, 0, base_declaration_name); @@ -408,12 +447,22 @@ pub const TranslationUnit = struct { try unit.declaration_set.putNoClobber(allocator, declaration_index, list.items); switch (declaration.scope_type) { - .global => switch (module.types.array.get(declaration.type).*) { + .global => switch (module.types.array.get(declaration.getType()).*) { .function, .type, => {}, - .@"struct" => { - try unit.writeDeclaration(module, &unit.global_variable_declarations, allocator, declaration_index, 0, '_'); + .integer, + .@"struct", + .pointer, + => { + const new_separation_character: u8 = switch (module.types.array.get(declaration.getType()).*) { + .integer, + .@"struct", + => '_', + .pointer => ' ', + else => unreachable, + }; + try unit.writeDeclaration(module, &unit.global_variable_declarations, allocator, declaration_index, 0, new_separation_character); try unit.global_variable_declarations.append(allocator, ';'); try unit.global_variable_declarations.appendNTimes(allocator, '\n', 2); }, @@ -441,10 +490,10 @@ pub const TranslationUnit = struct { try list.append(allocator, '('); - if (function_prototype.arguments) |function_arguments| { - for (function_arguments) |argument_index| { + if (function_prototype.arguments.items.len > 0) { + for (function_prototype.arguments.items) |argument_index| { const arg_declaration = module.values.declarations.get(argument_index); - try unit.writeType(module, list, allocator, arg_declaration.type, ' '); + try unit.writeType(module, list, allocator, arg_declaration.getType(), ' '); try list.append(allocator, ' '); const arg_name = module.getName(arg_declaration.name).?; try list.appendSlice(allocator, arg_name); @@ -462,6 +511,7 @@ pub const TranslationUnit = struct { const function_definition = module.types.function_definitions.get(function_index); const function_prototype_type = module.types.array.get(function_definition.prototype); const function_prototype_index = function_prototype_type.function; + try unit.writeFunctionPrototype(module, list, allocator, function_prototype_index, name); return name; @@ -475,11 +525,17 @@ pub const TranslationUnit = struct { .noreturn => try list.appendSlice(allocator, "[[noreturn]] void"), .bool => try list.appendSlice(allocator, "bool"), .integer => |integer| { - try list.append(allocator, switch (integer.signedness) { - .signed => 's', - .unsigned => 'u', - }); - try list.writer(allocator).print("{}", .{integer.bit_count}); + if (type_index.eq(Type.usize)) { + try list.appendSlice(allocator, "usize"); + } else if (type_index.eq(Type.ssize)) { + try list.appendSlice(allocator, "ssize"); + } else { + try list.append(allocator, switch (integer.signedness) { + .signed => 's', + .unsigned => 'u', + }); + try list.writer(allocator).print("{}", .{integer.bit_count}); + } }, .pointer => { const name = try unit.cachePointerType(module, allocator, type_index, separation_character); @@ -516,6 +572,7 @@ pub const TranslationUnit = struct { .pointer => |pointer| { switch (module.types.array.get(pointer.element_type).*) { .function => |function| return try unit.writeFunctionPrototype(module, list, allocator, function, try std.mem.concat(allocator, u8, &.{ "(*", name, ")" })), + .optional => {}, else => |t| @panic(@tagName(t)), } }, @@ -529,11 +586,17 @@ pub const TranslationUnit = struct { fn writeAssembly(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, assembly_block_index: Compilation.Assembly.Block.Index, indentation: usize) !void { const assembly_block = module.values.assembly_blocks.get(assembly_block_index); + try list.appendSlice(allocator, "__asm__ __volatile__(\n"); + + var operand_values = ArrayList(Value.Index){}; + for (assembly_block.instructions) |instruction_index| { - const generic_instruction = module.values.assembly_instructions.get(instruction_index); try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); try list.append(allocator, '"'); + + const generic_instruction = module.values.assembly_instructions.get(instruction_index); + switch (module.descriptor.target.cpu.arch) { .x86_64 => { const architecture = @field(Compilation.Assembly, "x86_64"); @@ -541,20 +604,21 @@ pub const TranslationUnit = struct { const instruction: architecture.Instruction = @enumFromInt(generic_instruction.id); const instruction_name = switch (instruction) { .@"and" => "andq", + .mov => "movq", .xor => @tagName(instruction), .call => "callq", }; try list.appendSlice(allocator, instruction_name); assert(generic_instruction.operands.len <= 2); + if (generic_instruction.operands.len > 0) { try list.append(allocator, ' '); - var operand_i: usize = generic_instruction.operands.len; - while (operand_i > 0) { - operand_i -= 1; + var skip = false; - const operand = generic_instruction.operands[operand_i]; + var operand_iterator = std.mem.reverseIterator(generic_instruction.operands); + while (operand_iterator.next()) |operand| { switch (operand) { .register => |generic_register| { const register: architecture.Register = @enumFromInt(generic_register); @@ -565,10 +629,29 @@ pub const TranslationUnit = struct { try list.writer(allocator).print("$0x{x}", .{number_literal}); }, .value_index => |value_index| { - try unit.writeValue(module, list, allocator, Type.Index.invalid, indentation + 1, .{ - .value_index = value_index, - .type_index = Type.Index.invalid, - }); + const operand_value = module.values.array.get(value_index); + + switch (operand_value.*) { + .unary_operation => |unary_operation_index| { + const unary_operation = module.values.unary_operations.get(unary_operation_index); + switch (unary_operation.id) { + .address_of => try list.writer(allocator).print("%{}", .{operand_values.items.len}), + else => |t| @panic(@tagName(t)), + } + }, + .function_definition => { + try unit.writeValue(module, list, allocator, Type.Index.invalid, indentation + 1, .{ + .value_index = value_index, + .type_index = Type.Index.invalid, + }); + skip = true; + }, + else => |t| @panic(@tagName(t)), + } + + if (!skip) { + try operand_values.append(allocator, value_index); + } }, } @@ -581,9 +664,33 @@ pub const TranslationUnit = struct { }, else => unreachable, } + try list.appendSlice(allocator, "\\n\\t\"\n"); } + if (operand_values.items.len > 0) { + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ":\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ": "); + + for (operand_values.items) |operand_value_index| { + try list.appendSlice(allocator, "\"x\"("); + + try unit.writeValue(module, list, allocator, Type.Index.invalid, indentation + 1, .{ + .value_index = operand_value_index, + .type_index = Type.Index.invalid, + }); + + try list.appendSlice(allocator, "), "); + } + + _ = list.pop(); + _ = list.pop(); + + try list.append(allocator, '\n'); + } + try list.appendNTimes(allocator, ' ', indentation * margin_width); try list.append(allocator, ')'); } @@ -593,6 +700,7 @@ pub const TranslationUnit = struct { assert(t.* == .@"struct"); const result = if (unit.struct_type_set.get(type_index, separation_character)) |r| r else blk: { const type_name = try unit.renderTypeName(module, allocator, type_index); + // if (std.mem.containsAtLeast(u8, type_name, 1, "Process")) @breakpoint(); logln(.c, .g, "Registering struct {s}: #{}", .{ type_name, type_index.uniqueInteger() }); try unit.struct_type_set.put(allocator, type_index, .{ .underscore = type_name, @@ -623,7 +731,7 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, " : "); try list.writer(allocator).print("{}", .{module.types.array.get(struct_field.type).getBitSize()}); }, - true => try unit.writeCDeclaration(module, &list, allocator, struct_field_name, struct_field.type, '_'), + true => try unit.writeCDeclaration(module, &list, allocator, struct_field_name, struct_field.type, ' '), } try list.appendSlice(allocator, ";\n"); @@ -885,86 +993,233 @@ pub const TranslationUnit = struct { return result; } - fn writeSyscall(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, syscall_index: Compilation.Syscall.Index, function_return_type: Type.Index, indentation: usize) !void { - const syscall = module.values.syscalls.get(syscall_index); - const arguments = syscall.getArguments(); + const ValueType = enum { + statement, + expression, + }; - if (!unit.syscall_bitset.isSet(arguments.len)) { - try unit.function_declarations.appendSlice(allocator, "static __inline u64 syscall"); - try unit.function_declarations.writer(allocator).print("{}(", .{arguments.len}); - try unit.function_declarations.appendSlice(allocator, "u64 n, "); - for (0..arguments.len) |arg_i| { - try unit.function_declarations.writer(allocator).print("u64 arg{}, ", .{arg_i}); - } - _ = unit.function_declarations.pop(); - _ = unit.function_declarations.pop(); - try unit.function_declarations.appendSlice(allocator, - \\) { - \\ - ); + fn writeIntrinsic(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, intrinsic_index: Compilation.Intrinsic.Index, function_return_type: Type.Index, indentation: usize, value_type: ValueType) !void { + const intrinsic = module.values.intrinsics.get(intrinsic_index); + switch (intrinsic.kind) { + .syscall => |syscall_arguments| { + const syscall_argument_count = syscall_arguments.items.len; + if (!unit.syscall_bitset.isSet(syscall_argument_count - 1)) { + try unit.function_declarations.appendSlice(allocator, "u64 syscall"); + try unit.function_declarations.writer(allocator).print("{}(", .{syscall_argument_count}); + for (0..syscall_argument_count) |arg_i| { + try unit.function_declarations.writer(allocator).print("u64 arg{}, ", .{arg_i}); + } + _ = unit.function_declarations.pop(); + _ = unit.function_declarations.pop(); + try unit.function_declarations.appendSlice(allocator, + \\) { + \\ + ); - const simple_register_argument_count = @min(arguments.len, 3); - const complex_register_argument_count = arguments.len - simple_register_argument_count; - const simple_argument_registers = [_]u8{ 'D', 'S', 'd' }; - const complex_argument_registers = [_]u8{ 10, 8, 9 }; + const simple_register_argument_count = @min(syscall_argument_count, 4); + const complex_register_argument_count = syscall_argument_count - simple_register_argument_count; + const simple_argument_registers = [_]u8{ 'a', 'D', 'S', 'd' }; + const complex_argument_registers = [_]u8{ 10, 8, 9 }; - for (0..complex_register_argument_count) |i| { - try unit.function_declarations.appendNTimes(allocator, ' ', indentation * margin_width); - try unit.function_declarations.writer(allocator).print("register unsigned long r{} __asm__(\"r{}\") = arg{};\n", .{ complex_argument_registers[i], complex_argument_registers[i], 3 + i }); + for (0..complex_register_argument_count) |i| { + try unit.function_declarations.appendNTimes(allocator, ' ', indentation * margin_width); + try unit.function_declarations.writer(allocator).print("register unsigned long r{} __asm__(\"r{}\") = arg{};\n", .{ complex_argument_registers[i], complex_argument_registers[i], simple_argument_registers.len + i }); + } + + try unit.function_declarations.appendSlice(allocator, + \\ unsigned long ret; + \\ + \\ __asm__ __volatile__("syscall" + \\ : "=a"(ret) + \\ : + ); + + for (0..simple_register_argument_count, simple_argument_registers[0..simple_register_argument_count]) |arg_i, arg_register| { + try unit.function_declarations.writer(allocator).print("\"{c}\"(arg{}), ", .{ arg_register, arg_i }); + } + + for (complex_argument_registers[0..complex_register_argument_count]) |arg_register| { + try unit.function_declarations.writer(allocator).print("\"r\"(r{}), ", .{arg_register}); + } + + _ = unit.function_declarations.pop(); + _ = unit.function_declarations.pop(); + + try unit.function_declarations.appendSlice(allocator, + \\ + \\ : "rcx", "r11", "memory" + \\ ); + \\ + \\ return ret; + \\} + \\ + \\ + ); + + unit.syscall_bitset.set(syscall_argument_count - 1); + } + + try list.writer(allocator).print("syscall{}(", .{syscall_argument_count}); + + for (syscall_arguments.items) |argument_index| { + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = argument_index, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ", "); + } + + _ = list.pop(); + _ = list.pop(); + try list.append(allocator, ')'); + }, + .cast => |cast_value_index| { + if (value_type == .statement) unreachable; + + const cast_type = intrinsic.type; + const cast_value = module.values.array.get(cast_value_index); + + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, cast_type, ' '); + try list.append(allocator, ')'); + + const cast_value_type = module.types.array.get(cast_value.getType(module)); + + switch (cast_value_type.*) { + .@"struct" => |struct_index| { + const struct_type = module.types.structs.get(struct_index); + switch (struct_type.backing_type.invalid) { + false => { + try list.appendSlice(allocator, "*("); + try unit.writeType(module, list, allocator, struct_type.backing_type, '_'); + try list.appendSlice(allocator, "*)&("); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = cast_value_index, + .type_index = function_return_type, + }); + try list.append(allocator, ')'); + }, + true => @panic("Unable to bitcast non-packed struct"), + } + }, + else => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = cast_value_index, + .type_index = Type.Index.invalid, + }), + } + }, + .optional_wrap => |value_to_wrap_index| { + const optional_type = module.types.array.get(intrinsic.type); + switch (optional_type.*) { + .optional => |optional| { + const optional_element_type = module.types.array.get(optional.element_type); + switch (optional_element_type.*) { + .pointer => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = value_to_wrap_index, + .type_index = optional.element_type, + }), + else => { + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, intrinsic.type, '_'); + try list.appendSlice(allocator, ") {\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".value = "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = value_to_wrap_index, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ",\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".is_null = false,\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.append(allocator, '}'); + }, + } + }, + else => |t| @panic(@tagName(t)), + } + }, + .array_coerce_to_slice => |array_to_coerce_value_index| { + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, intrinsic.type, '_'); + try list.appendSlice(allocator, ") {\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".ptr = "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = array_to_coerce_value_index, + .type_index = Type.Index.invalid, + }); + switch (module.values.array.get(array_to_coerce_value_index).*) { + .string_literal => {}, + else => try list.appendSlice(allocator, ".value"), + } + try list.appendSlice(allocator, ",\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + const array_value = module.values.array.get(array_to_coerce_value_index); + const array_type = module.types.array.get(array_value.getType(module)); + const array_length = switch (array_type.*) { + .array => |array| array.element_count, + .pointer => |pointer| switch (module.types.array.get(pointer.element_type).*) { + .array => |array| array.element_count, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }; + try list.writer(allocator).print(".len = {},\n", .{array_length}); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.append(allocator, '}'); + }, + .sign_extend, .zero_extend => |value_index| { + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = value_index, + .type_index = intrinsic.type, + }); + }, + .min => |binary| { + try list.appendSlice(allocator, try unit.cacheMacro(allocator, .MIN)); + try list.append(allocator, '('); + + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = binary.left, + .type_index = intrinsic.type, + }); + + try list.appendSlice(allocator, ", "); + + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = binary.right, + .type_index = intrinsic.type, + }); + + try list.append(allocator, ')'); + }, + else => |t| @panic(@tagName(t)), + } + } + + const Macro = enum { + MIN, + }; + + fn cacheMacro(unit: *TranslationUnit, allocator: Allocator, macro: Macro) ![]const u8 { + if (!unit.macro_set.contains(macro)) { + switch (macro) { + .MIN => { + try unit.macros.appendSlice(allocator, + \\#define MIN(a, b) \ + \\ ({ __typeof__ (a) _a = (a); \ + \\ __typeof__ (b) _b = (b); \ + \\ _a < _b ? _a : _b; }) + \\ + \\ + ); + }, } - try unit.function_declarations.appendSlice(allocator, - \\ unsigned long ret; - \\ - \\ __asm__ __volatile__("syscall" - \\ : "=a"(ret) - \\ : "a"(n), - ); - - for (0..simple_register_argument_count, simple_argument_registers[0..simple_register_argument_count]) |arg_i, arg_register| { - try unit.function_declarations.writer(allocator).print("\"{c}\"(arg{}), ", .{ arg_register, arg_i }); - } - - for (complex_argument_registers[0..complex_register_argument_count]) |arg_register| { - try unit.function_declarations.writer(allocator).print("\"r\"(r{}), ", .{arg_register}); - } - - _ = unit.function_declarations.pop(); - _ = unit.function_declarations.pop(); - - try unit.function_declarations.appendSlice(allocator, - \\ - \\ : "rcx", "r11", "memory" - \\ ); - \\ - \\ return ret; - \\} - \\ - \\ - ); - - unit.syscall_bitset.set(arguments.len); + unit.macro_set.setPresent(macro, true); } - try list.writer(allocator).print("syscall{}(", .{arguments.len}); - - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = syscall.number, - .type_index = function_return_type, - }); - try list.appendSlice(allocator, ", "); - - for (arguments) |argument_index| { - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = argument_index, - .type_index = Type.Index.invalid, - }); - try list.appendSlice(allocator, ", "); - } - - _ = list.pop(); - _ = list.pop(); - try list.append(allocator, ')'); + return @tagName(macro); } fn writeUnreachable(list: *ArrayList(u8), allocator: Allocator) !void { @@ -974,9 +1229,18 @@ pub const TranslationUnit = struct { fn writeCall(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, call_index: Compilation.Call.Index, function_return_type: Type.Index, indentation: usize) !void { const call = module.values.calls.get(call_index); const call_value = module.values.array.get(call.value); + var argument_declarations = ArrayList(Compilation.Declaration.Index){}; switch (call_value.*) { .function_definition => |function_definition_index| { const name = try unit.renderFunctionName(module, allocator, function_definition_index); + // if (equal(u8, name, "os_execute")) { + // @breakpoint(); + // } + const function_definition = module.types.function_definitions.get(function_definition_index); + const function_prototype_type = module.types.array.get(function_definition.prototype); + const function_prototype = module.types.function_prototypes.get(function_prototype_type.function); + argument_declarations = function_prototype.arguments; + try list.appendSlice(allocator, name); try list.append(allocator, '('); }, @@ -987,11 +1251,24 @@ pub const TranslationUnit = struct { .type_index = function_return_type, }); - const left_type = module.types.array.get(module.values.array.get(field_access.declaration_reference).declaration_reference.type); + const left_type = module.types.array.get(module.values.array.get(field_access.declaration_reference).declaration_reference.getType(module)); + // const is_pointer = switch (left_type) { + // .pointer => |pointer| b: { + // break :b true; + // }, + // }; const is_pointer = switch (left_type.*) { .pointer => true, else => false, }; + const container_field = module.types.container_fields.get(module.values.field_accesses.get(field_access_index).field); + argument_declarations = switch (module.types.array.get(container_field.type).*) { + .pointer => |pointer| switch (module.types.array.get(pointer.element_type).*) { + .function => |function| module.types.function_prototypes.get(function).arguments, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }; if (is_pointer) { try list.appendSlice(allocator, "->"); @@ -1009,12 +1286,17 @@ pub const TranslationUnit = struct { if (!call.arguments.invalid) { const argument_list = module.values.argument_lists.get(call.arguments); + assert(argument_declarations.items.len == argument_list.array.items.len); if (argument_list.array.items.len > 0) { - for (argument_list.array.items) |argument_index| { + for (argument_list.array.items, argument_declarations.items, 0..) |argument_index, argument_declaration_index, index| { + _ = index; + + const argument_declaration = module.values.declarations.get(argument_declaration_index); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = argument_index, - .type_index = Type.Index.invalid, + .type_index = argument_declaration.getType(), }); try list.appendSlice(allocator, ", "); } @@ -1027,6 +1309,39 @@ pub const TranslationUnit = struct { try list.append(allocator, ')'); } + fn writeBranch(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, branch_index: Compilation.Branch.Index, function_return_type: Type.Index, indentation: usize) !bool { + const branch = module.values.branches.get(branch_index); + try list.appendSlice(allocator, "if ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.expression, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ") "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.taken_expression, + .type_index = function_return_type, + }); + + if (!branch.not_taken_expression.invalid) { + if (module.values.array.get(branch.taken_expression).* == .block) { + _ = list.pop(); + try list.appendSlice(allocator, " else "); + } else { + unreachable; + } + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.not_taken_expression, + .type_index = function_return_type, + }); + + if (module.values.array.get(branch.not_taken_expression).* == .block) { + return false; + } + } + + return true; + } + const ValueArguments = struct { value_index: Value.Index, type_index: Type.Index, @@ -1035,7 +1350,6 @@ pub const TranslationUnit = struct { fn writeValue(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_return_type: Type.Index, indentation: usize, arguments: ValueArguments) anyerror!void { const value_index = arguments.value_index; const type_index = arguments.type_index; - _ = type_index; const value = module.values.array.get(value_index); //logln(.c, .g, "Generating C code for {s}", .{@tagName(value.*)}); switch (value.*) { @@ -1055,6 +1369,8 @@ pub const TranslationUnit = struct { }, .binary_operation => |binary_operation_index| { const binary_operation = module.values.binary_operations.get(binary_operation_index); + + try list.append(allocator, '('); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = binary_operation.left, .type_index = binary_operation.type, @@ -1074,6 +1390,7 @@ pub const TranslationUnit = struct { .shift_left => try list.appendSlice(allocator, "<<"), .shift_right => try list.appendSlice(allocator, ">>"), .compare_equal => try list.appendSlice(allocator, "=="), + .compare_not_equal => try list.appendSlice(allocator, "!="), .compare_greater_or_equal => try list.appendSlice(allocator, ">="), .compare_less_or_equal => try list.appendSlice(allocator, "<="), } @@ -1083,44 +1400,7 @@ pub const TranslationUnit = struct { .value_index = binary_operation.right, .type_index = binary_operation.type, }); - }, - .sign_extend => |cast_index| { - const sign_extend = module.values.casts.get(cast_index); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = sign_extend.value, - .type_index = sign_extend.type, - }); - }, - .cast => |cast_index| { - const cast = module.values.casts.get(cast_index); - try list.append(allocator, '('); - try unit.writeType(module, list, allocator, cast.type, ' '); try list.append(allocator, ')'); - const cast_value = module.values.array.get(cast.value); - const cast_value_type = module.types.array.get(cast_value.getType(module)); - - switch (cast_value_type.*) { - .@"struct" => |struct_index| { - const struct_type = module.types.structs.get(struct_index); - switch (struct_type.backing_type.invalid) { - false => { - try list.appendSlice(allocator, "*("); - try unit.writeType(module, list, allocator, struct_type.backing_type, '_'); - try list.appendSlice(allocator, "*)&("); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = cast.value, - .type_index = function_return_type, - }); - try list.append(allocator, ')'); - }, - true => @panic("Unable to bitcast non-packed struct"), - } - }, - else => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = cast.value, - .type_index = Type.Index.invalid, - }), - } }, .string_literal => |string_literal_descriptor| { try list.appendSlice(allocator, "(u8 *)"); @@ -1133,7 +1413,6 @@ pub const TranslationUnit = struct { try writeUnreachable(list, allocator); }, .call => |call_index| try unit.writeCall(module, list, allocator, call_index, function_return_type, indentation), - .syscall => |syscall_index| try unit.writeSyscall(module, list, allocator, syscall_index, function_return_type, indentation), .bool => |boolean| try list.appendSlice(allocator, if (boolean) "true" else "false"), .block => |block_index| try unit.writeBlock(module, list, allocator, block_index, function_return_type, indentation), .unary_operation => |unary_operation_index| { @@ -1145,17 +1424,93 @@ pub const TranslationUnit = struct { .pointer_dereference => '*', }; - try list.append(allocator, expression_character); + const emit_expression_character = blk: { + var emit = true; + if (unary_operation.id == .address_of) { + const unary_value_type = module.types.array.get(module.values.array.get(unary_operation.value).getType(module)); + switch (unary_value_type.*) { + .array => |source_array| if (!arguments.type_index.invalid) switch (module.types.array.get(arguments.type_index).*) { + .pointer => |pointer| switch (module.types.array.get(pointer.element_type).*) { + .array => {}, + .optional => switch (module.types.array.get(source_array.element_type).*) { + .optional => emit = false, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + .integer, + .@"struct", + .function, + .pointer, + => {}, + else => |t| @panic(@tagName(t)), + } + } + + break :blk emit; + }; + + if (emit_expression_character) { + try list.append(allocator, expression_character); + } + try list.append(allocator, '('); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = unary_operation.value, .type_index = unary_operation.type, }); try list.append(allocator, ')'); + + if (unary_operation.id == .address_of) { + if (!arguments.type_index.invalid) { + switch (module.types.array.get(arguments.type_index).*) { + .pointer => |pointer| switch (module.types.array.get(module.values.array.get(unary_operation.value).getType(module)).*) { + .array => switch (module.types.array.get(pointer.element_type).*) { + .array => {}, + .optional => try list.appendSlice(allocator, ".value"), + else => |t| @panic(@tagName(t)), + }, + .integer, + .@"struct", + .function, + => {}, + // .array, + // .optional, + // .integer, + // .@"struct", + // .function, + // => {}, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + } + } }, .container_initialization => |container_initialization_index| { const container_initialization = module.values.container_initializations.get(container_initialization_index); + // TODO: obliterate this crap and handle this in sema perfectly (optional_wrap) + const additional_indentation: usize = if (!type_index.invalid) blk: { + switch (module.types.array.get(type_index).*) { + .@"struct" => break :blk 0, + .optional => |optional| switch (module.types.array.get(optional.element_type).*) { + .pointer => break :blk 0, + else => { + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, type_index, '_'); + try list.appendSlice(allocator, ") {\n"); + try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); + try list.appendSlice(allocator, ".value = "); + break :blk 1; + }, + }, + else => |t| @panic(@tagName(t)), + } + } else 0; + try list.append(allocator, '('); try unit.writeType(module, list, allocator, container_initialization.type, '_'); try list.appendSlice(allocator, ") {\n"); @@ -1163,15 +1518,17 @@ pub const TranslationUnit = struct { const container_type = module.types.array.get(container_initialization.type); switch (container_type.*) { .@"struct" => { - const container_fields = module.types.structs.get(container_type.@"struct").fields; + const struct_type = module.types.structs.get(container_type.@"struct"); + logln(.c, .g, "Struct type: 0x{x}", .{@intFromPtr(struct_type)}); - for (container_initialization.field_initializations.items, container_fields.items) |field_initialization_index, container_field_index| { + for (container_initialization.field_initializations.items, struct_type.fields.items) |field_initialization_index, container_field_index| { try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); try list.append(allocator, '.'); const container_field = module.types.container_fields.get(container_field_index); const field_name = module.getName(container_field.name).?; try list.appendSlice(allocator, field_name); try list.appendSlice(allocator, " = "); + logln(.c, .g, "Name: {s}. Value: #{}. Field #{}", .{ field_name, field_initialization_index.uniqueInteger(), container_field_index.uniqueInteger() }); try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ .value_index = field_initialization_index, .type_index = container_field.type, @@ -1219,6 +1576,12 @@ pub const TranslationUnit = struct { }, else => |t| @panic(@tagName(t)), } + + if (additional_indentation > 0) { + try list.appendSlice(allocator, ",\n"); + try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); + try list.appendSlice(allocator, "}"); + } }, .array_initialization => |array_initialization_index| { const array_initialization = module.values.container_initializations.get(array_initialization_index); @@ -1278,13 +1641,14 @@ pub const TranslationUnit = struct { const sliceable = module.values.array.get(slice.sliceable); const sliceable_type_index = switch (sliceable.*) { - .declaration_reference => |declaration_reference| declaration_reference.type, + .declaration_reference => |declaration_reference| declaration_reference.getType(module), else => |t| @panic(@tagName(t)), }; const sliceable_type = module.types.array.get(sliceable_type_index); const sliceable_element_type = switch (sliceable_type.*) { .pointer => |pointer| pointer.element_type, .slice => |slice_type| slice_type.element_type, + .array => |array_type| array_type.element_type, else => |t| @panic(@tagName(t)), }; @@ -1322,6 +1686,19 @@ pub const TranslationUnit = struct { }); try list.appendSlice(allocator, "),\n"); }, + .array => { + try list.append(allocator, '('); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = slice.sliceable, + .type_index = sliceable_type_index, + }); + try list.appendSlice(allocator, ").value + ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = slice.range.start, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, "),\n"); + }, else => |t| @panic(@tagName(t)), } @@ -1349,7 +1726,7 @@ pub const TranslationUnit = struct { }, } }, - .slice => { + .slice, .array => { try list.append(allocator, '('); switch (slice.range.end.invalid) { false => { @@ -1368,6 +1745,9 @@ pub const TranslationUnit = struct { }); try list.appendSlice(allocator, ").len"); }, + .array => |array| { + try list.writer(allocator).print("{}", .{array.element_count}); + }, else => |t| @panic(@tagName(t)), } }, @@ -1460,6 +1840,22 @@ pub const TranslationUnit = struct { .slice => { try list.appendSlice(allocator, ".ptr"); }, + .array => try list.appendSlice(allocator, ".value"), + .pointer => |pointer| { + _ = pointer; + // logln(.c, .g, "TODO FIXME This looks wrong", .{}); + // try list.appendSlice(allocator, ".value"); + + // const element_type = module.types.array.get(pointer.element_type); + // switch (element_type.*) { + // .optional => |optional| { + // switch (module.types.array.get(optional.element_type).*) { + // else => |t| @panic(@tagName(t)), + // } + // }, + // else => |t| @panic(@tagName(t)), + // } + }, else => |t| @panic(@tagName(t)), } @@ -1470,69 +1866,11 @@ pub const TranslationUnit = struct { }); try list.append(allocator, ']'); }, - .optional_cast => |cast_index| { - const optional_cast = module.values.casts.get(cast_index); - const optional_type = module.types.array.get(optional_cast.type); - switch (optional_type.*) { - .optional => |optional| { - const optional_element_type = module.types.array.get(optional.element_type); - switch (optional_element_type.*) { - .pointer => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = optional_cast.value, - .type_index = optional.element_type, - }), - else => { - try list.append(allocator, '('); - try unit.writeType(module, list, allocator, optional_cast.type, '_'); - try list.appendSlice(allocator, ") {\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.appendSlice(allocator, ".value = "); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = optional_cast.value, - .type_index = Type.Index.invalid, - }); - try list.appendSlice(allocator, ",\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.appendSlice(allocator, ".is_null = false,\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.append(allocator, '}'); - }, - } - }, - else => |t| @panic(@tagName(t)), - } - }, - .undefined => try list.appendSlice(allocator, "{}"), - .array_coerce_to_slice => |cast_index| { - const array_coerce_to_slice = module.values.casts.get(cast_index); - try list.append(allocator, '('); - try unit.writeType(module, list, allocator, array_coerce_to_slice.type, '_'); - try list.appendSlice(allocator, ") {\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.appendSlice(allocator, ".ptr = "); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = array_coerce_to_slice.value, - .type_index = Type.Index.invalid, - }); - switch (module.values.array.get(array_coerce_to_slice.value).*) { - .string_literal => {}, - else => try list.appendSlice(allocator, ".value"), - } - try list.appendSlice(allocator, ",\n"); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - const array_value = module.values.array.get(array_coerce_to_slice.value); - const array_type = module.types.array.get(array_value.getType(module)); - const array_length = switch (array_type.*) { - .array => |array| array.element_count, - .pointer => |pointer| switch (module.types.array.get(pointer.element_type).*) { - .array => |array| array.element_count, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - }; - try list.writer(allocator).print(".len = {},\n", .{array_length}); - try list.appendNTimes(allocator, ' ', indentation * margin_width); - try list.append(allocator, '}'); + .undefined => { + // Assuming this is only done at initialization + _ = list.pop(); + _ = list.pop(); + _ = list.pop(); }, .enum_field => |enum_field_index| { const enum_field = module.types.enum_fields.get(enum_field_index); @@ -1541,6 +1879,13 @@ pub const TranslationUnit = struct { const enum_field_name = module.getName(enum_field.name).?; try list.appendSlice(allocator, enum_field_name); }, + .intrinsic => |intrinsic_index| try unit.writeIntrinsic(module, list, allocator, intrinsic_index, function_return_type, indentation, .expression), + .@"return" => |return_index| { + try unit.writeReturn(module, list, allocator, return_index, function_return_type, indentation); + }, + .branch => |branch_index| { + _ = try unit.writeBranch(module, list, allocator, branch_index, function_return_type, indentation); + }, else => |t| @panic(@tagName(t)), } } @@ -1555,7 +1900,7 @@ pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compil try unit.type_forward_declarations.append(allocator, '\n'); var offset: u64 = 0; - const slices = [_][]const u8{ unit.primitive_type_declarations.items, unit.type_forward_declarations.items, unit.type_declarations.items, unit.function_declarations.items, unit.global_variable_declarations.items, unit.string_literals.items, unit.function_definitions.items }; + const slices = [_][]const u8{ unit.primitive_type_declarations.items, unit.type_forward_declarations.items, unit.macros.items, unit.type_declarations.items, unit.function_declarations.items, unit.global_variable_declarations.items, unit.string_literals.items, unit.function_definitions.items }; for (slices) |slice| { try c_source_file.pwriteAll(slice, offset); offset += slice.len; @@ -1571,11 +1916,25 @@ pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compil var zig_command_line = ArrayList([]const u8){}; try zig_command_line.append(allocator, "zig"); try zig_command_line.append(allocator, "build-exe"); + + const local_cache_dir = std.fs.cwd().realpathAlloc(allocator, "zig-cache") catch b: { + std.fs.cwd().makeDir("nat/zig-cache") catch {}; + break :b try std.fs.cwd().realpathAlloc(allocator, "nat/zig-cache"); + }; + const home_directory = std.os.getenv("HOME") orelse @panic("Unable to get HOME environment variable. Did you forget to pass it to the process?"); + const global_cache_dir = try std.mem.concat(allocator, u8, &.{ home_directory, "/.cache/zig" }); + try zig_command_line.append(allocator, "--cache-dir"); + try zig_command_line.append(allocator, local_cache_dir); + try zig_command_line.append(allocator, "--global-cache-dir"); + try zig_command_line.append(allocator, global_cache_dir); + try zig_command_line.append(allocator, try std.mem.concat(allocator, u8, &.{ "-femit-bin=", descriptor.executable_path })); try zig_command_line.append(allocator, "-cflags"); + for (c_flags) |c_flag| { try zig_command_line.append(allocator, c_flag); } + try zig_command_line.append(allocator, "--"); try zig_command_line.append(allocator, c_source_file_realpath); @@ -1586,10 +1945,13 @@ pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compil switch (run_result.term) { .Exited => |exit_code| { if (exit_code != 0) { + std.debug.print("\nERROR: Zig command exited with code {}:\n", .{exit_code}); for (zig_command_line.items) |arg| { std.debug.print("{s} ", .{arg}); } - std.debug.panic("\nZig command exited with code {}:\n{s}", .{ exit_code, run_result.stderr }); + std.debug.print("\n\n{s}", .{run_result.stderr}); + + @panic("Internal error"); } }, else => |t| @panic(@tagName(t)), diff --git a/bootstrap/data_structures.zig b/bootstrap/data_structures.zig index d13932c..048f1c6 100644 --- a/bootstrap/data_structures.zig +++ b/bootstrap/data_structures.zig @@ -32,7 +32,7 @@ pub fn BlockList(comptime T: type) type { return struct { // TODO: make this not reallocate the whole block. Instead, use a pointer to the block as the ArrayList item - blocks: ArrayList(Block) = .{}, + blocks: ArrayList(*Block) = .{}, len: usize = 0, first_block: u32 = 0, @@ -136,7 +136,7 @@ pub fn BlockList(comptime T: type) type { const max_allocation = list.blocks.items.len * item_count; const result = switch (list.len < max_allocation) { true => blk: { - const block = &list.blocks.items[list.first_block]; + const block = list.blocks.items[list.first_block]; if (block.allocateIndex()) |element_index| { break :blk Index{ .element = element_index, @@ -148,13 +148,11 @@ pub fn BlockList(comptime T: type) type { }, false => blk: { const block_index = list.blocks.items.len; - const new_block = list.blocks.addOneAssumeCapacity(); + const new_block = try allocator.create(Block); new_block.* = .{}; + list.blocks.appendAssumeCapacity(new_block); const element_index = new_block.allocateIndex() catch unreachable; - const ptr = &new_block.items[element_index]; - _ = ptr; list.first_block += @intFromBool(block_index != 0); - break :blk Index{ .element = element_index, .block = @intCast(block_index), diff --git a/bootstrap/frontend/lexical_analyzer.zig b/bootstrap/frontend/lexical_analyzer.zig index 071ecd1..567c553 100644 --- a/bootstrap/frontend/lexical_analyzer.zig +++ b/bootstrap/frontend/lexical_analyzer.zig @@ -14,7 +14,8 @@ const File = Compilation.File; const logln = Compilation.logln; const fs = @import("../fs.zig"); -pub const Token = packed struct(u64) { +// TODO: switch to packed struct when speed is important +pub const Token = struct { start: u32, len: u24, id: Id, @@ -85,6 +86,7 @@ pub const Token = packed struct(u64) { fixed_keyword_cc = 0x98, fixed_keyword_for = 0x99, fixed_keyword_undefined = 0x9a, + fixed_keyword_break = 0x9b, }; pub const Index = u32; @@ -119,6 +121,7 @@ pub const FixedKeyword = enum { cc, @"for", undefined, + @"break", }; pub const Result = struct { diff --git a/bootstrap/frontend/semantic_analyzer.zig b/bootstrap/frontend/semantic_analyzer.zig index a8b4dcf..9494114 100644 --- a/bootstrap/frontend/semantic_analyzer.zig +++ b/bootstrap/frontend/semantic_analyzer.zig @@ -14,7 +14,7 @@ const Assignment = Compilation.Assignment; const Block = Compilation.Block; const Call = Compilation.Call; const Declaration = Compilation.Declaration; -const Enum = Compilation.Enum; +const Enum = Compilation.Type.Enum; const Field = Compilation.Field; const Function = Compilation.Function; const Intrinsic = Compilation.Intrinsic; @@ -25,13 +25,21 @@ const ScopeType = Compilation.ScopeType; const Slice = Compilation.Slice; const Struct = Compilation.Struct; const StringLiteral = Compilation.StringLiteral; +const Switch = Compilation.Switch; const Termination = Compilation.Type.Termination; const Type = Compilation.Type; +const ValidIntrinsic = Compilation.ValidIntrinsic; const Value = Compilation.Value; const log = Compilation.log; const logln = Compilation.logln; +const ExpectedArrayType = struct { + element_type: Type.Index, + len: ?usize = null, + termination: Termination, +}; + pub const Logger = enum { type, identifier, @@ -46,21 +54,23 @@ pub const Logger = enum { debug, fn_return_type, address_of, + reaches_end, pub var bitset = std.EnumSet(Logger).initMany(&.{ .type, - .identifier, + // .identifier, // .symbol_declaration, // .scope_node, // .node, // .typecheck, // .@"switch", // .block, - .call, + // .call, // // .scope_lookup, - .debug, - .fn_return_type, - .address_of, + // .debug, + // .fn_return_type, + // .address_of, + // .reaches_end, }); }; @@ -109,6 +119,8 @@ pub var unreachable_index = Value.Index.invalid; pub var optional_null_index = Value.Index.invalid; pub var pointer_null_index = Value.Index.invalid; pub var undefined_index = Value.Index.invalid; +pub var boolean_true = Value.Index.invalid; +pub var boolean_false = Value.Index.invalid; pub var pointer_to_any_type = Type.Index.invalid; pub var optional_pointer_to_any_type = Type.Index.invalid; @@ -213,7 +225,10 @@ const Analyzer = struct { analyzer.current_block = block_index; for (analyzer.payloads.items) |payload| { - const declaration_index = try analyzer.declarationCommon(scope_index, .local, payload.mutability, payload.name, payload.type, payload.value, null); + const declaration_type = Declaration.Type{ + .resolved = payload.type, + }; + const declaration_index = try analyzer.declarationCommon(scope_index, .local, payload.mutability, payload.name, declaration_type, payload.value, null); const statement_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = declaration_index, }); @@ -232,6 +247,12 @@ const Analyzer = struct { for (statement_nodes.items) |statement_node_index| { if (!reaches_end) { + switch (expect_type) { + .type_index => |type_index| if (!type_index.eq(Type.noreturn)) { + unreachable; + }, + else => |t| @panic(@tagName(t)), + } unreachable; } @@ -242,6 +263,7 @@ const Analyzer = struct { .assign, .add_assign => try analyzer.module.values.array.append(analyzer.allocator, try analyzer.processAssignment(scope_index, statement_node_index)), .@"unreachable" => blk: { reaches_end = false; + logln(.sema, .reaches_end, "Not reaching end because of unreachable", .{}); break :blk unreachable_index; }, .simple_symbol_declaration => blk: { @@ -261,6 +283,7 @@ const Analyzer = struct { }, .@"return" => blk: { reaches_end = false; + logln(.sema, .reaches_end, "Not reaching end because of unreachable", .{}); const return_expresssion = try analyzer.processReturn(scope_index, expect_type, statement_node_index); const return_value_index = try analyzer.module.values.array.append(analyzer.allocator, return_expresssion); @@ -273,6 +296,7 @@ const Analyzer = struct { .call = call_index, }); if (analyzer.getValueType(call_statement_index).eq(Type.noreturn)) { + logln(.sema, .reaches_end, "Not reaching end because of function call", .{}); reaches_end = false; } break :blk call_statement_index; @@ -281,7 +305,10 @@ const Analyzer = struct { .@"switch" => blk: { const switch_value = try analyzer.processSwitch(scope_index, expect_type, statement_node_index); switch (switch_value) { - .@"return" => reaches_end = false, + .@"return" => { + logln(.sema, .reaches_end, "Not reaching end because of return inside a switch statement", .{}); + reaches_end = false; + }, else => {}, } const switch_value_index = try analyzer.module.values.array.append(analyzer.allocator, switch_value); @@ -291,30 +318,43 @@ const Analyzer = struct { .if_else => blk: { const if_else_node_index = statement_node_index; const payload_node_index = Node.Index.invalid; - const if_else_value = try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index); - const branch = analyzer.module.values.branches.get(if_else_value.branch); - reaches_end = branch.reaches_end; - assert(if_else_value.maybe_payload_declaration_index == null); - const branch_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ - .branch = if_else_value.branch, - }); - break :blk branch_value_index; + switch (try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index)) { + .if_else => |if_else_value| { + const branch = analyzer.module.values.branches.get(if_else_value.branch); + reaches_end = branch.reaches_end; + if (!reaches_end) { + logln(.sema, .reaches_end, "Not reaching end because of branch statement", .{}); + } + assert(if_else_value.maybe_payload_declaration_index == null); + const branch_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .branch = if_else_value.branch, + }); + break :blk branch_value_index; + }, + .expression => |expression_value_index| break :blk expression_value_index, + } }, .if_else_payload => blk: { const if_else_node_index = statement_node.left; const payload_node_index = statement_node.right; - const if_else_value = try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index); + switch (try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index)) { + .if_else => |if_else_value| { + if (if_else_value.maybe_payload_declaration_index) |maybe_payload_declaration| { + try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); + } - if (if_else_value.maybe_payload_declaration_index) |maybe_payload_declaration| { - try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); + const branch = analyzer.module.values.branches.get(if_else_value.branch); + reaches_end = branch.reaches_end; + if (!reaches_end) { + logln(.sema, .reaches_end, "Not reaching end because of branch statement", .{}); + } + const branch_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .branch = if_else_value.branch, + }); + break :blk branch_statement_index; + }, + .expression => unreachable, } - - const branch = analyzer.module.values.branches.get(if_else_value.branch); - reaches_end = branch.reaches_end; - const branch_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ - .branch = if_else_value.branch, - }); - break :blk branch_statement_index; }, .@"if", .if_payload, @@ -333,20 +373,29 @@ const Analyzer = struct { const if_expression = try analyzer.processIf(scope_index, expect_type, if_statement_node_index, payload_node_index); - if (if_expression.maybe_payload_declaration_value) |maybe_payload_declaration| { - try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); - } + switch (if_expression.expression.invalid) { + // The condition is not evaluated at comptime, so emit both branches + false => { + if (if_expression.maybe_payload_declaration_value) |maybe_payload_declaration| { + try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); + } - const branch_index = try analyzer.module.values.branches.append(analyzer.allocator, .{ - .expression = if_expression.expression, - .taken_expression = if_expression.taken_expression, - .not_taken_expression = Value.Index.invalid, - .reaches_end = true, // The else branch, as it doesnt exist, always reaches the end - }); - const branch_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ - .branch = branch_index, - }); - break :blk branch_statement_index; + const branch_index = try analyzer.module.values.branches.append(analyzer.allocator, .{ + .expression = if_expression.expression, + .taken_expression = if_expression.taken_expression, + .not_taken_expression = Value.Index.invalid, + .reaches_end = true, // The else branch, as it doesnt exist, always reaches the end + }); + const branch_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .branch = branch_index, + }); + break :blk branch_statement_index; + }, + true => switch (if_expression.taken_expression.invalid) { + true => continue, + false => break :blk if_expression.taken_expression, + }, + } }, .compiler_intrinsic => blk: { const intrinsic_value = try analyzer.compilerIntrinsic(scope_index, ExpectType.none, statement_node_index); @@ -372,6 +421,8 @@ const Analyzer = struct { }); break :blk value_index; }, + // TODO: analyze further + .break_expression => try analyzer.module.values.array.append(analyzer.allocator, .@"break"), else => |t| @panic(@tagName(t)), }; @@ -425,6 +476,9 @@ const Analyzer = struct { .identifier => .{ .value_index = try analyzer.doIdentifier(scope_index, ExpectType.none, operand_node.token, scope_index), }, + .address_of => .{ + .value_index = try analyzer.module.values.array.append(analyzer.allocator, try analyzer.addressOf(scope_index, ExpectType.none, operand_node_index)), + }, else => |t| @panic(@tagName(t)), }; operand_list.appendAssumeCapacity(operand); @@ -469,9 +523,9 @@ const Analyzer = struct { logln(.sema, .call, "Method object valid: {}", .{!method_object.invalid}); if (!method_object.invalid) { - const first_argument_index = function_prototype.arguments.?[0]; + const first_argument_index = function_prototype.arguments.items[0]; const first_argument = analyzer.module.values.declarations.get(first_argument_index); - const first_argument_type = first_argument.type; + const first_argument_type = first_argument.getType(); const method_object_value = analyzer.module.values.array.get(method_object); const method_object_type = method_object_value.getType(analyzer.module); // TODO: further typecheck @@ -509,81 +563,82 @@ const Analyzer = struct { argument_array.appendAssumeCapacity(method_object_argument); } - if (function_prototype.arguments) |argument_declarations| { - logln(.sema, .call, "Argument declaration count: {}. Argument node list count: {}", .{ argument_declarations.len, call_argument_node_list.len }); + logln(.sema, .call, "Argument declaration count: {}. Argument node list count: {}", .{ function_prototype.arguments.items.len, call_argument_node_list.len }); + if (function_prototype.arguments.items.len == call_argument_count) { + for (function_prototype.arguments.items[method_object_count..], call_argument_node_list, 0..) |argument_declaration_index, argument_node_index, _index| { + const index = _index + method_object_count; + const argument_declaration = analyzer.module.values.declarations.get(argument_declaration_index); + const argument_node = analyzer.getScopeNode(scope_index, argument_node_index); + const value_node_index = switch (argument_node.id) { + .identifier => blk: { + const identifier = analyzer.tokenIdentifier(scope_index, argument_node.token); + const identifier_hash = try analyzer.processIdentifier(identifier); - if (argument_declarations.len == call_argument_count) { - for (argument_declarations[method_object_count..], call_argument_node_list, 0..) |argument_declaration_index, argument_node_index, _index| { - const index = _index + method_object_count; - const argument_declaration = analyzer.module.values.declarations.get(argument_declaration_index); - const argument_node = analyzer.getScopeNode(scope_index, argument_node_index); - const value_node_index = switch (argument_node.id) { - .identifier => blk: { - const identifier = analyzer.tokenIdentifier(scope_index, argument_node.token); - const identifier_hash = try analyzer.processIdentifier(identifier); - - if (identifier_hash == argument_declaration.name) { - break :blk argument_node_index; - } else { - const call_site_name = analyzer.module.getName(identifier_hash).?; - const definition_site_name = analyzer.module.getName(argument_declaration.name).?; - // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; - std.debug.panic("At function call, argument #{} must be named the same way. Call site was name '{s}' while function definition has it named as '{s}'", .{ index, call_site_name, definition_site_name }); - } - }, - .named_argument => blk: { - const identifier_node = analyzer.getScopeNode(scope_index, argument_node.left); - if (identifier_node.id != .identifier) { - @panic("expected identifier"); - } - const identifier = analyzer.tokenIdentifier(scope_index, identifier_node.token); - const identifier_hash = try analyzer.processIdentifier(identifier); - - if (identifier_hash == argument_declaration.name) { - break :blk argument_node.right; - } else { - const call_site_name = analyzer.module.getName(identifier_hash).?; - const definition_site_name = analyzer.module.getName(argument_declaration.name).?; - // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; - std.debug.panic("At function call, argument #{} must be named the same way. Call site was name '{s}' while function definition has it named as '{s}'", .{ index, call_site_name, definition_site_name }); - } - }, - else => |node_id| { + if (identifier_hash == argument_declaration.name) { + break :blk argument_node_index; + } else { + const call_site_name = analyzer.module.getName(identifier_hash).?; const definition_site_name = analyzer.module.getName(argument_declaration.name).?; // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; + std.debug.panic("At function call, argument #{} must be named the same way. Call site was name '{s}' while function definition has it named as '{s}'", .{ index, call_site_name, definition_site_name }); + } + }, + .named_argument => blk: { + const identifier_node = analyzer.getScopeNode(scope_index, argument_node.left); + if (identifier_node.id != .identifier) { + @panic("expected identifier"); + } + const identifier = analyzer.tokenIdentifier(scope_index, identifier_node.token); + const identifier_hash = try analyzer.processIdentifier(identifier); - std.debug.panic("Argument #{} of call to function of type {s} must be named as '{s}'", .{ index, @tagName(node_id), definition_site_name }); - }, - }; - const call_argument_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ - .type_index = argument_declaration.type, - }, value_node_index); - const call_site_type = analyzer.getValueType(call_argument_value_index); - const result = try analyzer.typeCheck(ExpectType{ - .type_index = argument_declaration.type, - }, call_site_type); + if (identifier_hash == argument_declaration.name) { + break :blk argument_node.right; + } else { + const call_site_name = analyzer.module.getName(identifier_hash).?; + const definition_site_name = analyzer.module.getName(argument_declaration.name).?; + // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; + std.debug.panic("At function call, argument #{} must be named the same way. Call site was name '{s}' while function definition has it named as '{s}'", .{ index, call_site_name, definition_site_name }); + } + }, + else => |node_id| { + const definition_site_name = analyzer.module.getName(argument_declaration.name).?; + // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; - argument_array.appendAssumeCapacity(switch (result) { - .array_coerce_to_slice => blk: { - const array_coerce_to_slice = try analyzer.module.values.casts.append(analyzer.allocator, .{ - .value = call_argument_value_index, - .type = argument_declaration.type, - }); - const coertion_value = try analyzer.module.values.array.append(analyzer.allocator, .{ - .array_coerce_to_slice = array_coerce_to_slice, - }); - break :blk coertion_value; - }, - else => |t| @panic(@tagName(t)), - .success => call_argument_value_index, - }); - } - } else { - panic("{s} call has argument count mismatch: call has {}, function declaration has {}", .{ switch (method_object.invalid) { - true => "Function", - false => "Method function", - }, call_argument_count, argument_declarations.len }); + std.debug.panic("Argument #{} of call to function of type {s} must be named as '{s}'", .{ index, @tagName(node_id), definition_site_name }); + }, + }; + const argument_declaration_type = argument_declaration.getType(); + const argument_expect_type = ExpectType{ + .type_index = argument_declaration_type, + }; + const call_argument_value_index = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, value_node_index); + const call_site_type = analyzer.getValueType(call_argument_value_index); + const result = try analyzer.typeCheck(argument_expect_type, call_site_type); + + argument_array.appendAssumeCapacity(switch (result) { + .array_coerce_to_slice => blk: { + const array_coerce_to_slice = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .array_coerce_to_slice = call_argument_value_index, + }, + .type = argument_declaration_type, + }); + + const coertion_value = try analyzer.module.values.array.append(analyzer.allocator, .{ + .intrinsic = array_coerce_to_slice, + }); + + break :blk coertion_value; + }, + else => |t| @panic(@tagName(t)), + .success => call_argument_value_index, + }); } + } else { + panic("{s} call has argument count mismatch: call has {}, function declaration has {}", .{ switch (method_object.invalid) { + true => "Function", + false => "Method function", + }, call_argument_count, function_prototype.arguments.items.len }); } return argument_array; @@ -710,7 +765,7 @@ const Analyzer = struct { return call_index; } - fn typeCheckEnumLiteral(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index, enum_type: *const Enum) !?Enum.Field.Index { + fn typeCheckEnumLiteral(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index, enum_type: Enum) !?Enum.Field.Index { const enum_name = tokenBytes(analyzer.getScopeToken(scope_index, token_index), analyzer.getScopeSourceFile(scope_index)); const enum_name_hash = try analyzer.processIdentifier(enum_name); @@ -727,6 +782,82 @@ const Analyzer = struct { } } + const TypeCheckSwitchEnums = struct { + switch_case_groups: ArrayList(ArrayList(Enum.Field.Index)), + else_switch_case_group_index: ?usize = null, + }; + + fn typecheckSwitchEnums(analyzer: *Analyzer, scope_index: Scope.Index, enum_type: Type.Enum, switch_case_node_list: []const Node.Index) !TypeCheckSwitchEnums { + var result = TypeCheckSwitchEnums{ + .switch_case_groups = try ArrayList(ArrayList(Enum.Field.Index)).initCapacity(analyzer.allocator, switch_case_node_list.len), + }; + + var existing_enums = ArrayList(Enum.Field.Index){}; + + for (switch_case_node_list, 0..) |switch_case_node_index, index| { + const switch_case_node = analyzer.getScopeNode(scope_index, switch_case_node_index); + + switch (switch_case_node.left.invalid) { + false => { + const switch_case_condition_node = analyzer.getScopeNode(scope_index, switch_case_node.left); + var switch_case_group = ArrayList(Enum.Field.Index){}; + + switch (switch_case_condition_node.id) { + .enum_literal => { + if (try typeCheckEnumLiteral(analyzer, scope_index, switch_case_condition_node.token + 1, enum_type)) |enum_field_index| { + for (existing_enums.items) |existing| { + if (enum_field_index.eq(existing)) { + // Duplicate case + unreachable; + } + } + + try switch_case_group.append(analyzer.allocator, enum_field_index); + try existing_enums.append(analyzer.allocator, enum_field_index); + } else { + unreachable; + } + }, + .node_list => { + const node_list = analyzer.getScopeNodeList(scope_index, switch_case_condition_node); + try switch_case_group.ensureTotalCapacity(analyzer.allocator, node_list.items.len); + + for (node_list.items) |case_condition_node_index| { + const case_condition_node = analyzer.getScopeNode(scope_index, case_condition_node_index); + switch (case_condition_node.id) { + .enum_literal => { + if (try typeCheckEnumLiteral(analyzer, scope_index, case_condition_node.token + 1, enum_type)) |enum_field_index| { + for (existing_enums.items) |existing| { + if (enum_field_index.eq(existing)) { + // Duplicate case + unreachable; + } + } + + try existing_enums.append(analyzer.allocator, enum_field_index); + switch_case_group.appendAssumeCapacity(enum_field_index); + } else { + unreachable; + } + }, + else => |t| @panic(@tagName(t)), + } + } + }, + else => |t| @panic(@tagName(t)), + } + + result.switch_case_groups.appendAssumeCapacity(switch_case_group); + }, + true => { + result.else_switch_case_group_index = index; + }, + } + } + + return result; + } + fn processSwitch(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value { const node = analyzer.getScopeNode(scope_index, node_index); assert(node.id == .@"switch"); @@ -748,73 +879,9 @@ const Analyzer = struct { const enum_field_name = analyzer.module.getName(e_field.name); _ = enum_field_name; - var existing_enums = ArrayList(Enum.Field.Index){}; - var switch_case_groups = try ArrayList(ArrayList(Enum.Field.Index)).initCapacity(analyzer.allocator, switch_case_node_list.len); - var else_switch_case_group = false; + const typecheck_enum_result = try analyzer.typecheckSwitchEnums(scope_index, enum_type.*, switch_case_node_list); - for (switch_case_node_list, 0..) |switch_case_node_index, index| { - _ = index; - const switch_case_node = analyzer.getScopeNode(scope_index, switch_case_node_index); - - switch (switch_case_node.left.invalid) { - false => { - const switch_case_condition_node = analyzer.getScopeNode(scope_index, switch_case_node.left); - var switch_case_group = ArrayList(Enum.Field.Index){}; - - switch (switch_case_condition_node.id) { - .enum_literal => { - if (try typeCheckEnumLiteral(analyzer, scope_index, switch_case_condition_node.token + 1, enum_type)) |enum_field_index| { - for (existing_enums.items) |existing| { - if (enum_field_index.eq(existing)) { - // Duplicate case - unreachable; - } - } - - try switch_case_group.append(analyzer.allocator, enum_field_index); - try existing_enums.append(analyzer.allocator, enum_field_index); - } else { - unreachable; - } - }, - .node_list => { - const node_list = analyzer.getScopeNodeList(scope_index, switch_case_condition_node); - try switch_case_group.ensureTotalCapacity(analyzer.allocator, node_list.items.len); - - for (node_list.items) |case_condition_node_index| { - const case_condition_node = analyzer.getScopeNode(scope_index, case_condition_node_index); - switch (case_condition_node.id) { - .enum_literal => { - if (try typeCheckEnumLiteral(analyzer, scope_index, case_condition_node.token + 1, enum_type)) |enum_field_index| { - for (existing_enums.items) |existing| { - if (enum_field_index.eq(existing)) { - // Duplicate case - unreachable; - } - } - - try existing_enums.append(analyzer.allocator, enum_field_index); - switch_case_group.appendAssumeCapacity(enum_field_index); - } else { - unreachable; - } - }, - else => |t| @panic(@tagName(t)), - } - } - }, - else => |t| @panic(@tagName(t)), - } - - switch_case_groups.appendAssumeCapacity(switch_case_group); - }, - true => { - else_switch_case_group = true; - }, - } - } - - const group_index = for (switch_case_groups.items, 0..) |switch_case_group, switch_case_group_index| { + const group_index = for (typecheck_enum_result.switch_case_groups.items, 0..) |switch_case_group, switch_case_group_index| { break for (switch_case_group.items) |enum_field_index| { if (e_field_index.eq(enum_field_index)) { break switch_case_group_index; @@ -822,9 +889,7 @@ const Analyzer = struct { } else { continue; }; - } else { - unreachable; - }; + } else typecheck_enum_result.else_switch_case_group_index orelse unreachable; logln(.sema, .@"switch", "Index: {}", .{group_index}); @@ -833,10 +898,50 @@ const Analyzer = struct { return analyzer.module.values.array.get(result_index).*; }, + .declaration_reference => |declaration_reference| { + switch (analyzer.module.types.array.get(declaration_reference.getType(analyzer.module)).*) { + .@"enum" => |enum_index| { + const enum_type = analyzer.module.types.enums.get(enum_index); + const typecheck_enum_result = try analyzer.typecheckSwitchEnums(scope_index, enum_type.*, switch_case_node_list); + + var group_list = try ArrayList(Switch.Group).initCapacity(analyzer.allocator, switch_case_node_list.len); + + for (switch_case_node_list, typecheck_enum_result.switch_case_groups.items) |case_node_index, case_enum_group| { + const case_node = analyzer.getScopeNode(scope_index, case_node_index); + const expression_node_index = case_node.right; + const expression_value_index = try analyzer.unresolvedAllocate(scope_index, expect_type, expression_node_index); + var value_list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, case_enum_group.items.len); + + for (case_enum_group.items) |case_enum_item| { + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .enum_field = case_enum_item, + }); + + value_list.appendAssumeCapacity(value_index); + } + + group_list.appendAssumeCapacity(.{ + .conditions = value_list, + .expression = expression_value_index, + }); + } + + const switch_expression = try analyzer.module.values.switches.append(analyzer.allocator, .{ + .value = switch_expression_value_index, + .groups = group_list, + }); + + const switch_value = Value{ + .switch_expression = switch_expression, + }; + + return switch_value; + }, + else => |t| @panic(@tagName(t)), + } + }, else => |t| @panic(@tagName(t)), } - - unreachable; } fn range(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Range { @@ -870,13 +975,12 @@ const Analyzer = struct { } fn whileLoop(analyzer: *Analyzer, parent_scope_index: Scope.Index, expect_type: ExpectType, while_node_index: Node.Index) !Loop.Index { - _ = expect_type; const while_loop_node = analyzer.getScopeNode(parent_scope_index, while_node_index); assert(while_loop_node.id == .simple_while); // TODO: complete const scope_index = parent_scope_index; const condition_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.boolean, while_loop_node.left); - const body_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.boolean, while_loop_node.right); + const body_index = try analyzer.unresolvedAllocate(scope_index, expect_type, while_loop_node.right); const reaches_end = switch (analyzer.module.values.array.get(body_index).*) { .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, else => |t| @panic(@tagName(t)), @@ -925,7 +1029,10 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), }; const payload_name = if (maybe_payload_name) |name| name else "_"; - const declaration_index = try analyzer.declarationCommon(scope_index, .local, .@"var", payload_name, Type.usize, for_range.start, null); + const declaration_type = Declaration.Type{ + .resolved = Type.usize, + }; + const declaration_index = try analyzer.declarationCommon(scope_index, .local, .@"var", payload_name, declaration_type, for_range.start, null); const declaration_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = declaration_index, }); @@ -998,6 +1105,97 @@ const Analyzer = struct { return loop_index; } + fn evaluateComptimeValue(analyzer: *Analyzer, value_index: Value.Index) ?Value.Index { + const value = analyzer.module.values.array.get(value_index); + return switch (value.*) { + .declaration_reference => |declaration_reference| blk: { + const declaration_index = declaration_reference.value; + const declaration = analyzer.module.values.declarations.get(declaration_index); + + if (declaration.mutability == .@"const") { + if (!declaration.init_value.invalid) { + break :blk analyzer.evaluateComptimeValue(declaration.init_value); + } + } + + break :blk null; + }, + .intrinsic => |intrinsic_index| blk: { + const intrinsic = analyzer.module.values.intrinsics.get(intrinsic_index); + break :blk switch (intrinsic.kind) { + .cast => null, + else => |t| @panic(@tagName(t)), + }; + }, + .unary_operation => |unary_operation_index| blk: { + const unary_operation = analyzer.module.values.unary_operations.get(unary_operation_index); + if (analyzer.evaluateComptimeValue(unary_operation.value)) |_| { + unreachable; + } else { + break :blk null; + } + }, + .binary_operation => |binary_operation_index| blk: { + const binary_operation = analyzer.module.values.binary_operations.get(binary_operation_index); + if (analyzer.evaluateComptimeValue(binary_operation.left)) |left_index| { + if (analyzer.evaluateComptimeValue(binary_operation.right)) |right_index| { + switch (binary_operation.id) { + .compare_equal => { + if (left_index.eq(right_index)) break :blk boolean_true; + const left = analyzer.module.values.array.get(left_index); + const right = analyzer.module.values.array.get(right_index); + + switch (left.*) { + .enum_field => if (left.enum_field.eq(right.enum_field)) { + break :blk boolean_true; + } else { + break :blk boolean_false; + }, + else => |t| @panic(@tagName(t)), + } + }, + else => |t| @panic(@tagName(t)), + } + + unreachable; + } + } + + break :blk null; + }, + // TODO: delete optional unwrap as value + .optional_unwrap => |optional_unwrap_index| blk: { + const optional_unwrap = analyzer.module.values.optional_unwraps.get(optional_unwrap_index); + if (analyzer.evaluateComptimeValue(optional_unwrap.value)) |_| { + unreachable; + } else { + break :blk null; + } + }, + // TODO: support comptime code? + .field_access, + .call, + .slice_access, + => null, + .enum_field => value_index, + .indexed_access => |indexed_access_index| blk: { + const indexed_access = analyzer.module.values.indexed_accesses.get(indexed_access_index); + if (analyzer.evaluateComptimeValue(indexed_access.indexed_expression)) |comptime_indexed| { + _ = comptime_indexed; + + if (analyzer.evaluateComptimeValue(indexed_access.index_expression)) |comptime_index| { + _ = comptime_index; + + unreachable; + } + } + + break :blk null; + }, + else => |t| @panic(@tagName(t)), + }; + } + const If = struct { maybe_payload_declaration_value: ?Value.Index, expression: Value.Index, @@ -1016,6 +1214,7 @@ const Analyzer = struct { const if_branch_node = analyzer.getScopeNode(scope_index, if_node_index); // analyzer.debugNode(scope_index, if_node_index); assert(if_branch_node.id == .@"if"); + var if_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ .type_index = switch (payload_node_index.invalid) { true => Type.boolean, @@ -1023,6 +1222,28 @@ const Analyzer = struct { }, }, if_branch_node.left); + if (payload_node_index.invalid) { + if (analyzer.evaluateComptimeValue(if_expression_index)) |comptime_evaluated_condition| { + if (comptime_evaluated_condition.eq(boolean_true)) { + return If{ + .maybe_payload_declaration_value = null, + .expression = Value.Index.invalid, + .taken_expression = try analyzer.unresolvedAllocate(scope_index, expect_type, if_branch_node.right), + .reaches_end = true, + }; + } else if (comptime_evaluated_condition.eq(boolean_false)) { + return If{ + .maybe_payload_declaration_value = null, + .expression = Value.Index.invalid, + .taken_expression = Value.Index.invalid, + .reaches_end = true, + }; + } else { + @panic("internal error"); + } + } + } + const maybe_payload_declaration_value_index: ?Value.Index = if (!payload_node_index.invalid) blk: { const if_type_index = analyzer.getValueType(if_expression_index); logln(.sema, .fn_return_type, "If condition expression has type #{}", .{if_type_index.uniqueInteger()}); @@ -1037,12 +1258,16 @@ const Analyzer = struct { } const result: ?Value.Index = if (maybe_payload_name) |payload_name| b: { - // TODO: + const maybe_payload_declaration_type = Declaration.Type{ + .resolved = if_type_index, + }; + const maybe_payload_declaration_index = try analyzer.declarationCommon(scope_index, .local, .@"const", try std.fmt.allocPrint(analyzer.allocator, "maybe_{}_{s}", .{ maybe: { const r = analyzer.maybe_count; analyzer.maybe_count += 1; break :maybe r; - }, payload_name }), if_type_index, if_expression_index, null); + }, payload_name }), maybe_payload_declaration_type, if_expression_index, null); + const maybe_payload_declaration_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = maybe_payload_declaration_index, }); @@ -1050,7 +1275,6 @@ const Analyzer = struct { if_expression_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration_reference = .{ .value = maybe_payload_declaration_index, - .type = if_type_index, }, }); @@ -1102,9 +1326,12 @@ const Analyzer = struct { return if_result; } - const IfElseResult = struct { - maybe_payload_declaration_index: ?Value.Index, - branch: Compilation.Branch.Index, + const IfElseResult = union(enum) { + if_else: struct { + maybe_payload_declaration_index: ?Value.Index, + branch: Compilation.Branch.Index, + }, + expression: Value.Index, }; fn processIfElse(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index, payload_node_index: Node.Index) !IfElseResult { @@ -1114,24 +1341,38 @@ const Analyzer = struct { assert(!node.right.invalid); const if_result = try analyzer.processIf(scope_index, expect_type, node.left, payload_node_index); - const not_taken_expression_index = try analyzer.unresolvedAllocate(scope_index, expect_type, node.right); - const false_reaches_end = switch (analyzer.module.values.array.get(not_taken_expression_index).*) { - .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, - else => |t| @panic(@tagName(t)), - }; - const reaches_end = if_result.reaches_end and false_reaches_end; + switch (if_result.expression.invalid) { + // The condition is not evaluated at comptime, so emit both branches + false => { + const not_taken_expression_index = try analyzer.unresolvedAllocate(scope_index, expect_type, node.right); + const false_reaches_end = switch (analyzer.module.values.array.get(not_taken_expression_index).*) { + .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, + .branch => |branch_index| analyzer.module.values.branches.get(branch_index).reaches_end, + else => |t| @panic(@tagName(t)), + }; + const reaches_end = if_result.reaches_end or false_reaches_end; - const branch_index = try analyzer.module.values.branches.append(analyzer.allocator, .{ - .expression = if_result.expression, - .taken_expression = if_result.taken_expression, - .not_taken_expression = not_taken_expression_index, - .reaches_end = reaches_end, - }); + const branch_index = try analyzer.module.values.branches.append(analyzer.allocator, .{ + .expression = if_result.expression, + .taken_expression = if_result.taken_expression, + .not_taken_expression = not_taken_expression_index, + .reaches_end = reaches_end, + }); - return IfElseResult{ - .maybe_payload_declaration_index = if_result.maybe_payload_declaration_value, - .branch = branch_index, - }; + return IfElseResult{ + .if_else = .{ + .maybe_payload_declaration_index = if_result.maybe_payload_declaration_value, + .branch = branch_index, + }, + }; + }, + true => return .{ + .expression = switch (if_result.taken_expression.invalid) { + true => try analyzer.unresolvedAllocate(scope_index, expect_type, node.right), + false => if_result.taken_expression, + }, + }, + } } fn processAssignment(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Value { @@ -1150,8 +1391,10 @@ const Analyzer = struct { // const id = analyzer.tokenIdentifier(.token); // logln("id: {s}", .{id}); const left = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const left_type_index = analyzer.getValueType(left); + assert(!left_type_index.invalid); const right = try analyzer.unresolvedAllocate(scope_index, ExpectType{ - .type_index = analyzer.getValueType(left), + .type_index = left_type_index, }, node.right); if (analyzer.module.values.array.get(left).isComptime(analyzer.module) and analyzer.module.values.array.get(right).isComptime(analyzer.module)) { @@ -1208,6 +1451,7 @@ const Analyzer = struct { .shift_left => .shift_left, .shift_right => .shift_right, .compare_equal => .compare_equal, + .compare_not_equal => .compare_not_equal, .compare_greater_than => .compare_greater_than, .compare_greater_or_equal => .compare_greater_or_equal, .compare_less_than => .compare_less_than, @@ -1216,6 +1460,7 @@ const Analyzer = struct { }; const left_expect_type: ExpectType = switch (binary_operation_id) { .compare_equal, + .compare_not_equal, .compare_less_or_equal, .compare_less_than, .compare_greater_than, @@ -1244,6 +1489,7 @@ const Analyzer = struct { .compare_greater_or_equal, .compare_greater_than, .compare_less_or_equal, + .compare_not_equal, => ExpectType{ .type_index = analyzer.getValueType(left_index), }, @@ -1256,7 +1502,11 @@ const Analyzer = struct { .right = right_index, .type = switch (expect_type) { .none => switch (binary_operation_id) { - .bit_and => left_type, + .bit_and, + .shift_right, + .sub, + .multiply, + => left_type, else => |t| @panic(@tagName(t)), }, .type_index => |type_index| type_index, @@ -1312,26 +1562,122 @@ const Analyzer = struct { return null; } + fn addressOf(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value { + const node = analyzer.getScopeNode(scope_index, node_index); + + const pointer_expect_type: ExpectType = switch (expect_type) { + .none => expect_type, + .type_index => |type_index| .{ + .addressable = switch (analyzer.module.types.array.get(type_index).*) { + .pointer => |pointer| pointer, + .slice => |slice| .{ + .@"const" = slice.@"const", + .many = true, + .element_type = slice.element_type, + .termination = slice.termination, + }, + else => |t| @panic(@tagName(t)), + }, + }, + .flexible_integer => unreachable, + else => unreachable, + }; + + const appointee_value_index = try analyzer.unresolvedAllocate(scope_index, pointer_expect_type, node.left); + const addressable = switch (pointer_expect_type) { + .none => switch (analyzer.module.values.array.get(appointee_value_index).*) { + .declaration_reference => |*declaration_reference| switch (analyzer.module.types.array.get(declaration_reference.getType(analyzer.module)).*) { + .integer => ExpectType.Addressable{ + .element_type = declaration_reference.getType(analyzer.module), + .many = false, + .@"const" = false, + .termination = .none, + }, + .pointer => |pointer| pointer, + .any => { + // TODO + // const declaration = analyzer.module.values.declarations.get(declaration_reference.value); + // switch (analyzer.module.types.array.get(declaration.type).*) { + // else => |t| @panic(@tagName(t)), + // } + unreachable; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + .addressable => |addressable| addressable, + else => |t| @panic(@tagName(t)), + }; + // + const unary_type_index: Type.Index = try analyzer.getPointerType(.{ + .element_type = analyzer.module.values.array.get(appointee_value_index).getType(analyzer.module), + .many = addressable.many, + .@"const" = addressable.@"const", + .termination = addressable.termination, + }); + + const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ + .id = .address_of, + .value = appointee_value_index, + .type = unary_type_index, + }); + + const value: Value = switch (expect_type) { + .none => .{ + .unary_operation = unary_index, + }, + .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { + .slice => b: { + const array_coerce_to_slice = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .array_coerce_to_slice = appointee_value_index, + }, + .type = type_index, + }); + break :b .{ + .intrinsic = array_coerce_to_slice, + }; + }, + else => .{ + .unary_operation = unary_index, + }, + }, + else => |t| @panic(@tagName(t)), + }; + + return value; + } + fn doIdentifierString(analyzer: *Analyzer, from_scope_index: Scope.Index, expect_type: ExpectType, identifier: []const u8, in_scope_index: Scope.Index) !Value.Index { logln(.sema, .identifier, "Referencing identifier: \"{s}\" from scope #{} in scope #{}", .{ identifier, from_scope_index.uniqueInteger(), in_scope_index.uniqueInteger() }); const identifier_hash = try analyzer.processIdentifier(identifier); - // if (equal(u8, identifier, "write")) { - // @breakpoint(); - // } - if (analyzer.lookupDeclarationInCurrentAndParentScopes(from_scope_index, identifier_hash)) |lookup| { const declaration_index = lookup.declaration; const declaration = analyzer.module.values.declarations.get(declaration_index); - // Up until now, only arguments have no initialization value - const typecheck_result = switch (declaration.init_value.invalid) { - false => blk: { + switch (declaration.init_value.invalid) { + false => { // logln(.sema, .identifier, "Declaration found: {}", .{init_value}); switch (analyzer.module.values.array.get(declaration.init_value).*) { .unresolved => |unresolved| { const previous_declaration = analyzer.current_declaration; analyzer.current_declaration = declaration_index; + + switch (declaration.type) { + .inferred => |inferred_type_index| assert(inferred_type_index.invalid), + .unresolved => |node_index| { + declaration.type = .{ + .resolved = try analyzer.resolveType(.{ + .scope_index = declaration.scope, + .node_index = node_index, + }), + }; + }, + else => |t| @panic(@tagName(t)), + } + try analyzer.resolveNode(declaration.init_value, lookup.scope, expect_type, unresolved.node_index); analyzer.current_declaration = previous_declaration; @@ -1351,137 +1697,121 @@ const Analyzer = struct { }, else => {}, } - - if (declaration.type.invalid) { - declaration.type = analyzer.module.values.array.get(declaration.init_value).getType(analyzer.module); - } - - // logln(.sema, .identifier, "Declaration resolved as: {}", .{init_value}); - // logln(.sema, .identifier, "Declaration mutability: {s}. Is comptime: {}", .{ @tagName(declaration.mutability), init_value.isComptime(analyzer.module) }); - - assert(!declaration.type.invalid); - // logln(.sema, .identifier, "About to typecheck identifier: \"{s}\"", .{identifier}); - const typecheck_result = try analyzer.typeCheck(expect_type, declaration.type); - // logln(.sema, .identifier, "Done typecheck identifier: \"{s}\"", .{identifier}); - - assert(!declaration.type.eq(pointer_to_any_type)); - assert(!declaration.type.eq(optional_pointer_to_any_type)); - assert(!declaration.type.eq(optional_any)); - - if (analyzer.module.values.array.get(declaration.init_value).isComptime(analyzer.module) and declaration.mutability == .@"const") { - assert(!declaration.init_value.invalid); - assert(typecheck_result == .success); - return declaration.init_value; - } - - break :blk typecheck_result; }, - true => try analyzer.typeCheck(expect_type, declaration.type), - }; - - const reference_index = try analyzer.module.values.array.append(analyzer.allocator, .{ - .declaration_reference = .{ - .value = declaration_index, - .type = switch (expect_type) { - .none => declaration.type, - .type_index => switch (typecheck_result) { - .success, - .cast_to_optional, - => expect_type.type_index, - .array_coerce_to_slice, - .zero_extend, - .sign_extend, - .take_source, - => declaration.type, - }, - .flexible_integer => blk: { - assert(!declaration.type.invalid); - break :blk declaration.type; - }, - .addressable => declaration.type, - .dereferenceable => unreachable, - }, - }, - }); - - return switch (typecheck_result) { - .success, - .take_source, - => reference_index, - inline .zero_extend, .sign_extend => |extend| blk: { - const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ - .value = reference_index, - .type = switch (expect_type) { - .flexible_integer => |flexible_integer| t: { - const cast_type = Type.Integer.getIndex(.{ - .signedness = switch (extend) { - .zero_extend => .unsigned, - .sign_extend => .signed, - else => unreachable, - }, - .bit_count = flexible_integer.byte_count << 3, - }); - break :t cast_type; - }, - else => |t| @panic(@tagName(t)), - }, - }); - - const value_index = try analyzer.module.values.array.append(analyzer.allocator, @unionInit(Value, @tagName(extend), cast_index)); - break :blk value_index; - }, - .cast_to_optional => blk: { - const cast_type = switch (expect_type) { - .type_index => |type_index| type_index, + true => { + switch (declaration.type) { + .resolved => {}, else => |t| @panic(@tagName(t)), - }; - const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ - .value = reference_index, - .type = cast_type, - }); - - const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ - .optional_cast = cast_index, - }); - break :blk value_index; - }, - .array_coerce_to_slice => blk: { - const cast_type = switch (expect_type) { - .type_index => |type_index| type_index, - else => |t| @panic(@tagName(t)), - }; - const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ - .value = reference_index, - .type = cast_type, - }); - - const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ - .array_coerce_to_slice = cast_index, - }); - - break :blk value_index; - }, - }; - } else { - logln(.sema, .type, "Identifier \"{s}\" not found as a declaration from scope #{} referenced in scope #{}", .{ identifier, from_scope_index.uniqueInteger(), in_scope_index.uniqueInteger() }); - const from_scope = analyzer.module.values.scopes.get(from_scope_index); - const scope_type = analyzer.module.types.array.get(from_scope.type); - switch (scope_type.*) { - .@"struct" => |struct_index| { - const struct_type = analyzer.module.types.structs.get(struct_index); - for (struct_type.fields.items) |struct_field_index| { - const struct_field = analyzer.module.types.container_fields.get(struct_field_index); - if (struct_field.name == identifier_hash) { - unreachable; - } - } else { - unreachable; } }, + } + + switch (declaration.type) { + .inferred => |*inferred_type_index| { + const declaration_value_type = analyzer.module.values.array.get(declaration.init_value).getType(analyzer.module); + switch (inferred_type_index.invalid) { + true => inferred_type_index.* = declaration_value_type, + false => {}, + } + }, + .resolved => {}, else => |t| @panic(@tagName(t)), } - unreachable; + const declaration_type_index = declaration.getType(); + const typecheck_result = try analyzer.typeCheck(expect_type, declaration_type_index); + + assert(!declaration_type_index.invalid); + assert(!declaration_type_index.eq(pointer_to_any_type)); + assert(!declaration_type_index.eq(optional_pointer_to_any_type)); + assert(!declaration_type_index.eq(optional_any)); + + if (!declaration.init_value.invalid and analyzer.module.values.array.get(declaration.init_value).isComptime(analyzer.module) and declaration.mutability == .@"const") { + assert(!declaration.init_value.invalid); + assert(typecheck_result == .success); + return declaration.init_value; + } else { + const declaration_reference = try analyzer.module.values.array.append(analyzer.allocator, .{ + .declaration_reference = .{ + .value = declaration_index, + }, + }); + + const result: Value.Index = switch (typecheck_result) { + .success, + .take_source, + => declaration_reference, + .optional_wrap => blk: { + const cast_type = switch (expect_type) { + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }; + const intrinsic_index = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .optional_wrap = declaration_reference, + }, + .type = cast_type, + }); + + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .intrinsic = intrinsic_index, + }); + break :blk value_index; + }, + inline .zero_extend, .sign_extend => |extension_type| blk: { + const intrinsic = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = @unionInit(Intrinsic.Kind, @tagName(extension_type), declaration_reference), + .type = switch (expect_type) { + .flexible_integer => |flexible_integer| t: { + const cast_type = Type.Integer.getIndex(.{ + .signedness = switch (extension_type) { + .zero_extend => .unsigned, + .sign_extend => .signed, + else => unreachable, + }, + .bit_count = flexible_integer.byte_count << 3, + }); + break :t cast_type; + }, + else => |t| @panic(@tagName(t)), + }, + }); + + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .intrinsic = intrinsic, + }); + break :blk value_index; + }, + else => |t| @panic(@tagName(t)), + }; + return result; + } + + // return switch (typecheck_result) { + // .success, + // .take_source, + // => reference_index, + // .array_coerce_to_slice => { + // const cast_type = switch (expect_type) { + // .type_index => |type_index| type_index, + // else => |t| @panic(@tagName(t)), + // }; + // _ = cast_type; + // unreachable; + // // const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ + // // .value = reference_index, + // // .type = cast_type, + // // }); + // // + // // const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + // // .array_coerce_to_slice = cast_index, + // // }); + // // + // // break :blk value_index; + // }, + // }; + } else { + panic("Identifier \"{s}\" not found as a declaration from scope #{} referenced in scope #{}", .{ identifier, from_scope_index.uniqueInteger(), in_scope_index.uniqueInteger() }); } } @@ -1491,8 +1821,6 @@ const Analyzer = struct { } fn resolveInteger(analyzer: *Analyzer, scope_index: Scope.Index, value_index: Value.Index) usize { - _ = scope_index; - const value = analyzer.module.values.array.get(value_index); return switch (value.*) { .declaration_reference => |declaration_reference| blk: { @@ -1500,6 +1828,15 @@ const Analyzer = struct { break :blk analyzer.resolveInteger(declaration.scope, declaration.init_value); }, .integer => |integer| integer.value, + .binary_operation => |binary_operation_index| { + const binary_operation = analyzer.module.values.binary_operations.get(binary_operation_index); + const left = analyzer.resolveInteger(scope_index, binary_operation.left); + const right = analyzer.resolveInteger(scope_index, binary_operation.right); + return switch (binary_operation.id) { + .add => left + right, + else => |t| @panic(@tagName(t)), + }; + }, else => |t| @panic(@tagName(t)), }; } @@ -1612,6 +1949,7 @@ const Analyzer = struct { const type_info = analyzer.module.types.array.get(type_index); break :a switch (type_info.*) { .integer => type_index, + .optional => |optional_type| optional_type.element_type, else => |t| @panic(@tagName(t)), }; }, @@ -1682,7 +2020,6 @@ const Analyzer = struct { => break :blk right_value.*, else => |t| @panic(@tagName(t)), } - // logln(.sema, .node, "Right: {}", .{right_value}); // struct_scope.declarations.get(identifier); @@ -1710,7 +2047,7 @@ const Analyzer = struct { std.debug.panic("LEFT: enum {s}. RIGHT: {s}", .{ enum_field_name, identifier }); }, .declaration_reference => |declaration_reference| { - const declaration_type = analyzer.module.types.array.get(declaration_reference.type); + const declaration_type = analyzer.module.types.array.get(declaration_reference.getType(analyzer.module)); switch (declaration_type.*) { .@"struct" => |struct_index| { @@ -1835,10 +2172,117 @@ const Analyzer = struct { unreachable; }, + .slice => |slice| { + _ = slice; + const slice_field = inline for (@typeInfo(Slice.Field).Enum.fields) |slice_field| { + if (equal(u8, slice_field.name, identifier)) { + break @field(Slice.Field, slice_field.name); + } + } else unreachable; + + const slice_access_type = switch (slice_field) { + .ptr => t: { + const slice_type_index = analyzer.getValueType(left_value_index); + const slice_type = analyzer.module.types.array.get(slice_type_index); + const slice_type_slice = slice_type.slice; + const pointer_type = try analyzer.getPointerType(.{ + .element_type = slice_type_slice.element_type, + .@"const" = slice_type_slice.@"const", + .many = true, + .termination = slice_type_slice.termination, + }); + break :t pointer_type; + }, + .len => Type.usize, + }; + + const field_access_index = try analyzer.module.values.slice_accesses.append(analyzer.allocator, .{ + .value = left_value_index, + .field = slice_field, + .type = slice_access_type, + }); + + break :blk Value{ + .slice_access = field_access_index, + }; + }, else => |t| @panic(@tagName(t)), } unreachable; }, + .call => |call_index| { + const call = analyzer.module.values.calls.get(call_index); + switch (analyzer.module.types.array.get(call.type).*) { + .@"struct" => |struct_index| { + const struct_type = analyzer.module.types.structs.get(struct_index); + + for (struct_type.fields.items) |struct_field_index| { + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); + if (struct_field.name == identifier_hash) { + const field_access_index = try analyzer.module.values.field_accesses.append(analyzer.allocator, .{ + .declaration_reference = left_value_index, + .field = struct_field_index, + }); + break :blk Value{ + .field_access = field_access_index, + }; + } + } else { + const scope1 = struct_type.scope; + const scope2 = scope_index; + const declaration_value = try analyzer.doIdentifier(scope1, ExpectType.none, node.right.value, scope2); + + const value_ref = analyzer.module.values.array.get(declaration_value); + break :blk value_ref.*; + } + + unreachable; + }, + else => |t| @panic(@tagName(t)), + } + unreachable; + }, + .indexed_access => |indexed_access_index| { + const indexed_access = analyzer.module.values.indexed_accesses.get(indexed_access_index); + const indexed_expression_value = analyzer.module.values.array.get(indexed_access.indexed_expression); + const indexed_expression_type = analyzer.module.types.array.get(indexed_expression_value.getType(analyzer.module)); + + const element_type_index = switch (indexed_expression_type.*) { + .array => |array_type| array_type.element_type, + else => |t| @panic(@tagName(t)), + }; + + switch (analyzer.module.types.array.get(element_type_index).*) { + .@"struct" => |struct_index| { + const struct_type = analyzer.module.types.structs.get(struct_index); + + for (struct_type.fields.items) |struct_field_index| { + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); + if (struct_field.name == identifier_hash) { + const field_access_index = try analyzer.module.values.field_accesses.append(analyzer.allocator, .{ + .declaration_reference = left_value_index, + .field = struct_field_index, + }); + break :blk Value{ + .field_access = field_access_index, + }; + } + } else { + const scope1 = struct_type.scope; + const scope2 = scope_index; + const declaration_value = try analyzer.doIdentifier(scope1, ExpectType.none, node.right.value, scope2); + + const value_ref = analyzer.module.values.array.get(declaration_value); + break :blk value_ref.*; + } + + unreachable; + }, + else => |t| @panic(@tagName(t)), + } + + unreachable; + }, else => |t| @panic(@tagName(t)), } }, @@ -1880,6 +2324,7 @@ const Analyzer = struct { .compare_greater_or_equal, .compare_less_than, .compare_less_or_equal, + .compare_not_equal, => try analyzer.processBinaryOperation(scope_index, expect_type, node_index), .expression_group => return try analyzer.resolveNode(value_index, scope_index, expect_type, node.left), //unreachable, .struct_type => blk: { @@ -1927,76 +2372,17 @@ const Analyzer = struct { .unary_operation = unary_index, }; }, - .address_of => blk: { - const many = false; - _ = many; - const addressable: ExpectType.Addressable = switch (expect_type) { - // .none => expect_type, - .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { - .pointer => |pointer| pointer, - .slice => |slice| .{ - .@"const" = slice.@"const", - .many = true, - .element_type = slice.element_type, - .termination = slice.termination, - }, - else => |t| @panic(@tagName(t)), - }, - .flexible_integer => unreachable, - else => unreachable, - }; - - const appointee_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ - .addressable = addressable, - }, node.left); - - const unary_type_index: Type.Index = try analyzer.getPointerType(.{ - .element_type = analyzer.module.values.array.get(appointee_value_index).getType(analyzer.module), - .many = addressable.many, - .@"const" = addressable.@"const", - .termination = addressable.termination, - }); - - const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ - .id = .address_of, - .value = appointee_value_index, - .type = unary_type_index, - }); - - const value: Value = switch (expect_type) { - .none => unreachable, - .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { - .slice => b: { - const array_coerce_to_slice = try analyzer.module.values.casts.append(analyzer.allocator, .{ - .value = appointee_value_index, - .type = type_index, - }); - break :b .{ - .array_coerce_to_slice = array_coerce_to_slice, - }; - }, - else => .{ - .unary_operation = unary_index, - }, - }, - .flexible_integer => unreachable, - else => unreachable, - }; - - break :blk value; - }, + .address_of => try analyzer.addressOf(scope_index, expect_type, node_index), .pointer_dereference => blk: { const new_expect_type = switch (expect_type) { .none => expect_type, - .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { - .pointer => unreachable, - else => unreachable, - // .type_index = try analyzer.getPointerType(.{ - // .element_type = type_index, - // .many = false, - // .@"const" = false, - // }), - // }, + .type_index => |type_index| ExpectType{ + .type_index = try analyzer.getPointerType(.{ + .element_type = type_index, + .@"const" = true, // TODO + .many = false, // TODO + .termination = .none, // TODO + }), }, .flexible_integer => unreachable, else => unreachable, @@ -2018,16 +2404,7 @@ const Analyzer = struct { .slice => blk: { const expression_to_slice_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); const expression_to_slice_type = analyzer.getValueType(expression_to_slice_index); - // const element_type = switch ) { - // .pointer => |pointer| pointer.element_type, - // .slice => |slice| slice.element_type, - // else => |t| @panic(@tagName(t)), - // }; - // const is_const = switch (analyzer.module.types.array.get(expression_to_slice_type).*) { - // .pointer => |pointer| pointer.@"const", - // .slice => |slice| slice.@"const", - // else => |t| @panic(@tagName(t)), - // }; + const slice_index = try analyzer.module.values.slices.append(analyzer.allocator, .{ .sliceable = expression_to_slice_index, .range = try analyzer.range(scope_index, node.right), @@ -2041,6 +2418,21 @@ const Analyzer = struct { .termination = pointer.termination, }, .slice => |slice| slice, + .array => |array| b: { + const is_const = switch (expect_type) { + .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { + .slice => |slice| slice.@"const", + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }; + + break :b .{ + .@"const" = is_const, + .element_type = array.element_type, + .termination = array.termination, + }; + }, else => |t| @panic(@tagName(t)), }), }); @@ -2051,9 +2443,20 @@ const Analyzer = struct { }, .indexed_access => blk: { const indexable_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + // const indexable_expression_type = switch (analyzer.module.values.array.get(indexable_expression_index).*) { + // .declaration_reference => |declaration_reference| declaration_reference.type, + // else => |t| @panic(@tagName(t)), + // }; const indexable_expression_type = analyzer.getValueType(indexable_expression_index); switch (analyzer.module.types.array.get(indexable_expression_type).*) { .slice => {}, + .array => {}, + .pointer => |pointer| { + switch (pointer.many) { + true => {}, + false => unreachable, // TODO: pointer to array? + } + }, else => |t| @panic(@tagName(t)), } const index_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ @@ -2075,7 +2478,7 @@ const Analyzer = struct { .type_index => |type_index| { switch (analyzer.module.types.array.get(type_index).*) { .@"enum" => |enum_index| { - const enum_type = analyzer.module.types.enums.get(enum_index); + const enum_type = analyzer.module.types.enums.get(enum_index).*; const enum_field_index = try analyzer.typeCheckEnumLiteral(scope_index, enum_literal_identifier_token, enum_type) orelse unreachable; break :blk .{ @@ -2089,30 +2492,7 @@ const Analyzer = struct { } }, .undefined => .undefined, - .anonymous_array_literal => blk: { - const array_element_type = switch (expect_type) { - .addressable => |addressable| addr: { - assert(addressable.many); - break :addr addressable.element_type; - }, - .type_index => |type_index| type_index, - else => |t| @panic(@tagName(t)), - }; - const expected_element_count: ?usize = null; - - const array_initialization = try analyzer.analyzeArrayLiteral(scope_index, array_element_type, node.left, expected_element_count); - break :blk .{ - .array_initialization = array_initialization, - }; - }, - .anonymous_container_literal => blk: { - assert(expect_type == .type_index); - const container_initialization = try analyzer.analyzeContainerLiteral(scope_index, expect_type.type_index, node.left); - break :blk .{ - .container_initialization = container_initialization, - }; - }, - .container_literal => blk: { + .empty_container_literal_guess, .container_literal => blk: { const list_nodes = analyzer.getScopeNodeList(scope_index, analyzer.getScopeNode(scope_index, node.right)); const literal_type = try analyzer.resolveType(.{ .scope_index = scope_index, @@ -2124,9 +2504,140 @@ const Analyzer = struct { .container_initialization = container_initialization, }; }, - .anonymous_array_element_initialization => { - try analyzer.resolveNode(value_index, scope_index, expect_type, node.left); - return; + .anonymous_container_literal => blk: { + const t = switch (expect_type) { + .type_index => |type_index| type_index, + .addressable => |addressable| addressable.element_type, + .none => Type.Index.invalid, + else => |t| @panic(@tagName(t)), + }; + analyzer.debugNode(scope_index, node_index); + const container_initialization = try analyzer.analyzeContainerLiteral(scope_index, t, node.right); + break :blk .{ + .container_initialization = container_initialization, + }; + }, + .array_literal => blk: { + const list_nodes = analyzer.getScopeNodeList(scope_index, analyzer.getScopeNode(scope_index, node.right)); + const literal_type = try analyzer.resolveType(.{ + .scope_index = scope_index, + .node_index = node.left, + .length_hint = list_nodes.items.len, + }); + + const array_initialization = try analyzer.analyzeArrayLiteral(scope_index, node.right, switch (analyzer.module.types.array.get(literal_type).*) { + .array => |array| .{ + .element_type = array.element_type, + .len = array.element_count, + .termination = array.termination, + }, + else => |t| @panic(@tagName(t)), + }); + + break :blk .{ + .array_initialization = array_initialization, + }; + }, + .anonymous_array_literal => blk: { + const expected_array_type = switch (expect_type) { + .addressable => |addressable| addr: { + assert(addressable.many); + break :addr .{ + .element_type = addressable.element_type, + .termination = addressable.termination, + }; + }, + .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { + .array => |array_type| .{ + .element_type = array_type.element_type, + .termination = array_type.termination, + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }; + + const array_initialization = try analyzer.analyzeArrayLiteral(scope_index, node.right, expected_array_type); + break :blk .{ + .array_initialization = array_initialization, + }; + }, + .optional_unwrap => blk: { + const optional_expect_type = ExpectType{ + .type_index = try analyzer.getOptionalType(switch (expect_type) { + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }), + }; + + const optional_value = try analyzer.unresolvedAllocate(scope_index, optional_expect_type, node.left); + + const optional_unwrap = try analyzer.module.values.optional_unwraps.append(analyzer.allocator, .{ + .value = optional_value, + }); + + const optional_unwrap_value = Value{ + .optional_unwrap = optional_unwrap, + }; + break :blk optional_unwrap_value; + }, + .anonymous_empty_literal => blk: { + const expected_type_index = switch (expect_type) { + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }; + switch (analyzer.module.types.array.get(expected_type_index).*) { + .@"struct" => |struct_index| { + const struct_type = analyzer.module.types.structs.get(struct_index); + var list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, struct_type.fields.items.len); + for (struct_type.fields.items) |struct_field_index| { + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); + + if (struct_field.default_value.invalid) { + unreachable; + } + + logln(.sema, .type, "Field name: {s}", .{analyzer.module.getName(struct_field.name).?}); + + switch (analyzer.module.values.array.get(struct_field.default_value).*) { + .unresolved => |unresolved| { + logln(.sema, .type, "IN node index: #{}", .{unresolved.node_index.uniqueInteger()}); + try analyzer.resolveNode(struct_field.default_value, struct_type.scope, ExpectType{ + .type_index = struct_field.type, + }, unresolved.node_index); + }, + else => {}, + } + + list.appendAssumeCapacity(struct_field.default_value); + } + + const container_initialization_index = try analyzer.module.values.container_initializations.append(analyzer.allocator, .{ + .field_initializations = list, + .type = expected_type_index, + }); + break :blk Value{ + .container_initialization = container_initialization_index, + }; + }, + else => |t| @panic(@tagName(t)), + } + }, + .usize_type => Value{ + .type = Type.usize, + }, + .if_else => blk: { + const if_else_node_index = node_index; + const payload_node_index = Node.Index.invalid; + switch (try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index)) { + .if_else => |if_else_value| { + assert(if_else_value.maybe_payload_declaration_index == null); + break :blk .{ + .branch = if_else_value.branch, + }; + }, + .expression => |expression_value_index| break :blk analyzer.module.values.array.get(expression_value_index).*, + } }, else => |t| @panic(@tagName(t)), }; @@ -2134,18 +2645,19 @@ const Analyzer = struct { analyzer.module.values.array.get(value_index).* = new_value; } - fn analyzeArrayLiteral(analyzer: *Analyzer, scope_index: Scope.Index, expected_element_type_index: Type.Index, node_list_node_index: Node.Index, expected_element_count: ?usize) !Compilation.ContainerInitialization.Index { + fn analyzeArrayLiteral(analyzer: *Analyzer, scope_index: Scope.Index, node_list_node_index: Node.Index, expected_array_type: ExpectedArrayType) !Compilation.ContainerInitialization.Index { const field_initialization_node_list = analyzer.getScopeNode(scope_index, node_list_node_index); const field_nodes = analyzer.getScopeNodeList(scope_index, field_initialization_node_list); - assert(!expected_element_type_index.invalid); + assert(!expected_array_type.element_type.invalid); const found_element_count = field_nodes.items.len; - const element_count = if (expected_element_count) |ec| if (ec == found_element_count) ec else @panic("Element count mismatch in array literal") else found_element_count; + const element_count = if (expected_array_type.len) |ec| if (ec == found_element_count) ec else @panic("Element count mismatch in array literal") else found_element_count; var list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, element_count); const element_expect_type = ExpectType{ - .type_index = expected_element_type_index, + .type_index = expected_array_type.element_type, }; + for (field_nodes.items) |element_node_index| { const array_element_value_index = try analyzer.unresolvedAllocate(scope_index, element_expect_type, element_node_index); list.appendAssumeCapacity(array_element_value_index); @@ -2156,8 +2668,8 @@ const Analyzer = struct { .field_initializations = list, .type = try analyzer.getArrayType(.{ .element_count = @intCast(element_count), - .element_type = expected_element_type_index, - .termination = unreachable, + .element_type = expected_array_type.element_type, + .termination = expected_array_type.termination, }), }); @@ -2180,7 +2692,7 @@ const Analyzer = struct { for (struct_type.fields.items) |struct_field_index| { const struct_field = analyzer.module.types.container_fields.get(struct_field_index); const struct_field_name = analyzer.module.getName(struct_field.name).?; - logln(.sema, .type, "struct field name in container literal: {s}", .{struct_field_name}); + // logln(.sema, .type, "struct field name in container literal: {s}", .{struct_field_name}); var value_index = Value.Index.invalid; @@ -2205,21 +2717,26 @@ const Analyzer = struct { if (value_index.invalid) { if (!struct_field.default_value.invalid) { - const default_value: Value.Index = switch (analyzer.module.values.array.get(struct_field.default_value).*) { - .unresolved => |unresolved| blk: { + switch (analyzer.module.values.array.get(struct_field.default_value).*) { + .unresolved => |unresolved| { + logln(.sema, .type, "Node index: #{}", .{unresolved.node_index.uniqueInteger()}); try analyzer.resolveNode(struct_field.default_value, struct_type.scope, ExpectType{ .type_index = struct_field.type, }, unresolved.node_index); - break :blk (&struct_field.default_value).*; }, - else => struct_field.default_value, - }; - value_index = default_value; + else => {}, + } + + assert(analyzer.module.values.array.get(struct_field.default_value).* != .unresolved); + + value_index = struct_field.default_value; } else { std.debug.panic("Field \"{s}\" forgotten in struct initialization", .{struct_field_name}); } } + logln(.sema, .type, "struct field {s}, container field #{}", .{ struct_field_name, struct_field_index.uniqueInteger() }); + list.appendAssumeCapacity(value_index); } @@ -2238,10 +2755,11 @@ const Analyzer = struct { unreachable; } + var list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, array_type.element_count); + const expect_type = ExpectType{ .type_index = array_type.element_type, }; - var list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, array_type.element_count); for (field_nodes.items) |array_element_node_index| { const element_value_index = try analyzer.unresolvedAllocate(scope_index, expect_type, array_element_node_index); @@ -2254,6 +2772,9 @@ const Analyzer = struct { }); return container_initialization_index; }, + .optional => |optional_type| { + return try analyzer.analyzeContainerLiteral(scope_index, optional_type.element_type, node_list_node_index); + }, else => |t| @panic(@tagName(t)), } } @@ -2273,18 +2794,13 @@ const Analyzer = struct { const string_literal_node = analyzer.getScopeNode(scope_index, node_index); assert(string_literal_node.id == .string_literal); const original_string_literal = analyzer.tokenStringLiteral(scope_index, string_literal_node.token); - const string_literal = blk: { - if (!analyzer.module.descriptor.transpile_to_c) { - for (original_string_literal) |ch| { - if (ch == '\\') { - break :blk try fixupStringLiteral(analyzer.allocator, original_string_literal); - } - } - } - - break :blk original_string_literal; + const fixed_string_literal = try fixupStringLiteral(analyzer.allocator, original_string_literal); + const string_literal = switch (analyzer.module.descriptor.transpile_to_c) { + true => original_string_literal, + false => fixed_string_literal, }; - const len: u32 = @intCast(string_literal.len); + + const len: u32 = @intCast(fixed_string_literal.len); const array_type_descriptor = Type.Array{ .element_type = Type.u8, .element_count = len, @@ -2308,7 +2824,7 @@ const Analyzer = struct { } fn fixupStringLiteral(allocator: Allocator, string_literal: []const u8) ![]const u8 { - var result = try ArrayList(u8).initCapacity(allocator, string_literal.len - 1); + var result = try ArrayList(u8).initCapacity(allocator, string_literal.len); var i: usize = 0; while (i < string_literal.len) : (i += 1) { @@ -2345,10 +2861,6 @@ const Analyzer = struct { const type_node = analyzer.getScopeNode(scope_index, node_index); const type_index: Type.Index = switch (type_node.id) { .identifier => blk: { - // const token = analyzer.getScopeToken(scope_index, type_node.token); - // const source_file = analyzer.getScopeSourceFile(scope_index); - // const identifier = tokenBytes(token, source_file); - // logln(.sema, .type, "Identifier: \"{s}\"", .{identifier}); const resolved_value_index = try analyzer.doIdentifier(scope_index, ExpectType.type, type_node.token, scope_index); const resolved_value = analyzer.module.values.array.get(resolved_value_index); break :blk switch (resolved_value.*) { @@ -2418,22 +2930,8 @@ const Analyzer = struct { .scope_index = scope_index, .node_index = type_node.left, }); - const gop = try analyzer.module.map.optionals.getOrPut(analyzer.allocator, element_type); - const result = switch (gop.found_existing) { - true => gop.value_ptr.*, - false => b: { - const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ - .optional = .{ - .element_type = element_type, - }, - }); - gop.value_ptr.* = type_index; - break :b gop.value_ptr.*; - }, - }; - - break :blk result; + break :blk try analyzer.getOptionalType(element_type); }, .pointer_type => blk: { const list_node = analyzer.getScopeNode(scope_index, type_node.left); @@ -2450,7 +2948,11 @@ const Analyzer = struct { .simple_function_prototype, .identifier, .unsigned_integer_type, + .signed_integer_type, .optional_type, + .array_type, + .usize_type, + .pointer_type, => { if (!type_index.invalid) { unreachable; @@ -2491,7 +2993,7 @@ const Analyzer = struct { var is_const = false; var type_index = Type.Index.invalid; - const termination = Termination.none; + var termination = Termination.none; for (node_list.items) |element_node_index| { const element_node = analyzer.getScopeNode(scope_index, element_node_index); @@ -2509,6 +3011,12 @@ const Analyzer = struct { }); }, .const_expression => is_const = true, + .zero_terminated => { + if (termination != .none) { + unreachable; + } + termination = .zero; + }, else => |t| @panic(@tagName(t)), } } @@ -2531,7 +3039,10 @@ const Analyzer = struct { for (node_list.items[0 .. node_list.items.len - 1]) |element_node_index| { const element_node = analyzer.getScopeNode(scope_index, element_node_index); switch (element_node.id) { - .identifier => { + .identifier, + .number_literal, + .add, + => { if (length_expression != null) { unreachable; } @@ -2580,9 +3091,10 @@ const Analyzer = struct { const arguments_node_index = simple_function_prototype_node.left; const return_type_node_index = simple_function_prototype_node.right; - const arguments: ?[]const Declaration.Index = switch (arguments_node_index.invalid) { - true => null, - false => blk: { + var argument_declarations = ArrayList(Declaration.Index){}; + switch (arguments_node_index.invalid) { + true => {}, + false => { const argument_list_node = analyzer.getScopeNode(scope_index, arguments_node_index); // logln("Function prototype argument list node: {}\n", .{function_prototype_node.left.uniqueInteger()}); const argument_node_list = switch (argument_list_node.id) { @@ -2592,31 +3104,29 @@ const Analyzer = struct { assert(argument_node_list.items.len > 0); if (argument_node_list.items.len > 0) { - var arguments = try ArrayList(Declaration.Index).initCapacity(analyzer.allocator, argument_node_list.items.len); + argument_declarations = try ArrayList(Declaration.Index).initCapacity(analyzer.allocator, argument_node_list.items.len); for (argument_node_list.items, 0..) |argument_node_index, index| { const argument_node = analyzer.getScopeNode(scope_index, argument_node_index); switch (argument_node.id) { .argument_declaration => { + const argument_name = analyzer.tokenIdentifier(scope_index, argument_node.token); const argument_type = try analyzer.resolveType(.{ .scope_index = scope_index, .node_index = argument_node.left, }); - const argument_name = analyzer.tokenIdentifier(scope_index, argument_node.token); - const argument_declaration = try analyzer.declarationCommon(scope_index, .local, .@"const", argument_name, argument_type, Value.Index.invalid, @intCast(index)); - - arguments.appendAssumeCapacity(argument_declaration); + const argument_declaration_type = Declaration.Type{ + .resolved = argument_type, + }; + const argument_declaration = try analyzer.declarationCommon(scope_index, .local, .@"const", argument_name, argument_declaration_type, Value.Index.invalid, @intCast(index)); + argument_declarations.appendAssumeCapacity(argument_declaration); }, else => |t| @panic(@tagName(t)), } } - - break :blk arguments.items; - } else { - break :blk null; } }, - }; + } const return_type = try analyzer.resolveType(.{ .scope_index = scope_index, @@ -2624,7 +3134,7 @@ const Analyzer = struct { }); return .{ - .arguments = arguments, + .arguments = argument_declarations, .return_type = return_type, }; } @@ -2821,8 +3331,13 @@ const Analyzer = struct { if (!analyzer.current_declaration.invalid) { const current_declaration = analyzer.module.values.declarations.get(analyzer.current_declaration); - assert(current_declaration.type.invalid); - current_declaration.type = Type.type; + switch (current_declaration.type) { + .inferred => |*inferred_type_index| { + assert(inferred_type_index.invalid); + inferred_type_index.* = Type.type; + }, + else => |t| @panic(@tagName(t)), + } } const count = blk: { @@ -2902,7 +3417,11 @@ const Analyzer = struct { const default_value = if (field_node.right.invalid) Value.Index.invalid else try analyzer.module.values.array.append(analyzer.allocator, .{ .unresolved = .{ - .node_index = field_node.right, + .node_index = blk: { + const def_node = analyzer.getScopeNode(scope_index, field_node.right); + assert(def_node.id != .node_list); + break :blk field_node.right; + }, }, }); @@ -2924,7 +3443,7 @@ const Analyzer = struct { { const enum_type_general = analyzer.module.types.array.get(container_type_index); const enum_type = analyzer.module.types.enums.get(enum_type_general.@"enum"); - enum_type.fields = try ArrayList(Compilation.Enum.Field.Index).initCapacity(analyzer.allocator, field_nodes.items.len); + enum_type.fields = try ArrayList(Compilation.Type.Enum.Field.Index).initCapacity(analyzer.allocator, field_nodes.items.len); } for (field_nodes.items) |field_node_index| { @@ -2969,7 +3488,7 @@ const Analyzer = struct { return container_type_index; } - fn declarationCommon(analyzer: *Analyzer, scope_index: Scope.Index, scope_type: ScopeType, mutability: Compilation.Mutability, name: []const u8, type_index: Type.Index, init_value: Value.Index, argument_index: ?u32) !Declaration.Index { + fn declarationCommon(analyzer: *Analyzer, scope_index: Scope.Index, scope_type: ScopeType, mutability: Compilation.Mutability, name: []const u8, declaration_type: Declaration.Type, init_value: Value.Index, argument_index: ?u32) !Declaration.Index { const identifier_index = try analyzer.processIdentifier(name); if (analyzer.lookupDeclarationInCurrentAndParentScopes(scope_index, identifier_index)) |lookup| { @@ -2985,7 +3504,7 @@ const Analyzer = struct { .scope_type = scope_type, .mutability = mutability, .init_value = init_value, - .type = type_index, + .type = declaration_type, .argument_index = argument_index, .scope = scope_index, }); @@ -3042,15 +3561,30 @@ const Analyzer = struct { }; assert(argument == null); - const type_index = switch (scope_type) { - .local => switch (expect_type) { - .type_index => |type_index| type_index, - else => analyzer.module.values.array.get(init_value_index).getType(analyzer.module), + const declaration_type: Declaration.Type = switch (declaration_node.left.invalid) { + false => switch (scope_type) { + .local => .{ + .resolved = expect_type.type_index, + }, + .global => .{ + .unresolved = declaration_node.left, + }, + }, + true => switch (scope_type) { + .local => .{ + .inferred = switch (expect_type) { + .none => analyzer.module.values.array.get(init_value_index).getType(analyzer.module), + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }, + }, + .global => .{ + .inferred = Type.Index.invalid, + }, }, - .global => Type.Index.invalid, }; - const result = try analyzer.declarationCommon(scope_index, scope_type, mutability, identifier, type_index, init_value_index, argument); + const result = try analyzer.declarationCommon(scope_index, scope_type, mutability, identifier, declaration_type, init_value_index, argument); return result; } @@ -3114,7 +3648,7 @@ const Analyzer = struct { zero_extend, sign_extend, take_source, - cast_to_optional, + optional_wrap, array_coerce_to_slice, }; @@ -3158,12 +3692,15 @@ const Analyzer = struct { unreachable; } }, + // TODO: integer cast + .integer => return .success, else => |t| @panic(@tagName(t)), }; }, .type_index => |type_index| blk: { if (source.eq(type_index)) { - unreachable; + // TODO: turn into a compiler error + return .success; } else { const destination_type = analyzer.module.types.array.get(type_index); const source_type = analyzer.module.types.array.get(source); @@ -3172,7 +3709,7 @@ const Analyzer = struct { .integer => |integer| switch (destination_type.*) { .optional => |optional| switch (analyzer.module.types.array.get(optional.element_type).*) { .pointer => if (integer.bit_count == 64) .success else unreachable, - .integer => .cast_to_optional, + .integer => .optional_wrap, else => |t| @panic(@tagName(t)), }, .integer => .success, @@ -3192,7 +3729,7 @@ const Analyzer = struct { const enum_type = analyzer.module.types.enums.get(enum_type_descriptor); if (!enum_type.backing_type.invalid) { if (enum_type.backing_type.eq(type_index)) { - unreachable; + return .success; } else { unreachable; } @@ -3202,6 +3739,9 @@ const Analyzer = struct { }, else => |t| @panic(@tagName(t)), }, + .slice => switch (destination_type.*) { + else => |t| @panic(@tagName(t)), + }, else => |t| @panic(@tagName(t)), }; } @@ -3240,6 +3780,8 @@ const Analyzer = struct { } }, .comptime_int => return TypeCheckResult.success, + .@"struct" => @panic("Expected int, have struct"), + .array => @panic("Expected int, have array"), else => |t| @panic(@tagName(t)), }, // TODO: type safety @@ -3257,6 +3799,20 @@ const Analyzer = struct { unreachable; } }, + .optional => |source_optional| switch (analyzer.module.types.array.get(destination_pointer.element_type).*) { + .optional => |destination_optional| { + if (destination_optional.element_type.eq(source_optional.element_type)) { + return .success; + } else { + unreachable; + } + }, + else => |t| @panic(@tagName(t)), + }, + .@"struct" => { + logln(.sema, .type, "Expected pointer to #{}, got pointer to #{} (pointer to struct)", .{ destination_pointer.element_type.uniqueInteger(), source_pointer.element_type.uniqueInteger() }); + unreachable; + }, else => |t| @panic(@tagName(t)), } } @@ -3334,24 +3890,24 @@ const Analyzer = struct { }, .pointer => |source_pointer| { if (destination_optional.element_type.eq(source)) { - return .cast_to_optional; + return .optional_wrap; } else { const destination_optional_element_type = analyzer.module.types.array.get(destination_optional.element_type); switch (destination_optional_element_type.*) { .pointer => |destination_pointer| { if (source.eq(optional_pointer_to_any_type)) { - return .cast_to_optional; + return .optional_wrap; } if (expected_type_index.eq(optional_pointer_to_any_type)) { - return .cast_to_optional; + return .optional_wrap; } if (destination_pointer.many == source_pointer.many) { if (destination_pointer.@"const" or destination_pointer.@"const" == source_pointer.@"const") { if (destination_pointer.element_type.eq(source_pointer.element_type)) { - return .cast_to_optional; + return .optional_wrap; } } } @@ -3374,17 +3930,18 @@ const Analyzer = struct { }, // TODO .integer => if (destination_optional.element_type.eq(source)) { - return .cast_to_optional; + return .optional_wrap; } else { + logln(.sema, .type, "Destination optional element type: #{}. Source: #{}", .{ destination_optional.element_type.uniqueInteger(), source.uniqueInteger() }); unreachable; }, .slice => |source_slice| if (destination_optional.element_type.eq(source)) { - return .cast_to_optional; + return .optional_wrap; } else { switch (analyzer.module.types.array.get(destination_optional.element_type).*) { .slice => |destination_slice| { if (destination_slice.element_type.eq(source_slice.element_type)) { - return .cast_to_optional; + return .optional_wrap; } else { unreachable; } @@ -3392,6 +3949,18 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), } }, + .array => |source_array| if (destination_optional.element_type.eq(source)) { + _ = source_array; + return .optional_wrap; + } else { + unreachable; + }, + .@"struct" => |source_struct| if (destination_optional.element_type.eq(source)) { + return .optional_wrap; + } else { + _ = source_struct; + unreachable; + }, else => |t| @panic(@tagName(t)), }, .function => |destination_function| switch (source_type.*) { @@ -3467,48 +4036,67 @@ const Analyzer = struct { false => unreachable, } }, + .comptime_int => return .success, else => |t| @panic(@tagName(t)), } }, .addressable => |addressable| { - const destination_type = analyzer.module.types.array.get(addressable.element_type); - const source_type = analyzer.module.types.array.get(source); + if (addressable.element_type.eq(source)) return .success else { + const destination_type = analyzer.module.types.array.get(addressable.element_type); + const source_type = analyzer.module.types.array.get(source); - switch (source_type.*) { - .array => |array| { - assert(addressable.many); - assert(addressable.termination == array.termination); + switch (source_type.*) { + .array => |source_array| { + assert(addressable.termination == source_array.termination); - if (array.element_type.eq(addressable.element_type)) { - return .success; - } else { - switch (destination_type.*) { - else => |t| @panic(@tagName(t)), + if (source_array.element_type.eq(addressable.element_type)) { + return .success; + } else { + switch (destination_type.*) { + .array => |destination_array| { + logln(.sema, .type, "SRC: {}. DST: {}", .{ source_array, destination_array }); + unreachable; + }, + else => |t| @panic(@tagName(t)), + } } - } - }, - .function => |source_function| { - assert(!addressable.many); - assert(addressable.termination == .none); - assert(addressable.@"const"); + }, + .function => |source_function| { + assert(!addressable.many); + assert(addressable.termination == .none); + assert(addressable.@"const"); - if (addressable.element_type.eq(source)) { - return .success; - } else { - switch (destination_type.*) { - .function => |destination_function| { - if (source_function.eq(destination_function)) { - return .success; - } else { - // TODO: FIXME - return .success; - } - }, - else => |t| @panic(@tagName(t)), + if (addressable.element_type.eq(source)) { + return .success; + } else { + switch (destination_type.*) { + .function => |destination_function| { + if (source_function.eq(destination_function)) { + return .success; + } else { + // TODO: FIXME + return .success; + } + }, + else => |t| @panic(@tagName(t)), + } } - } - }, - else => |t| @panic(@tagName(t)), + }, + .integer => |source_integer| { + assert(!addressable.many); + assert(addressable.termination == .none); + _ = source_integer; + if (addressable.element_type.eq(source)) { + return .success; + } else { + // TODO: hack + unreachable; + } + + unreachable; + }, + else => |t| @panic(@tagName(t)), + } } }, .dereferenceable => { @@ -3564,15 +4152,80 @@ const Analyzer = struct { return result; } + fn getOptionalType(analyzer: *Analyzer, element_type_index: Type.Index) !Type.Index { + const gop = try analyzer.module.map.optionals.getOrPut(analyzer.allocator, element_type_index); + if (!gop.found_existing) { + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ + .optional = .{ + .element_type = element_type_index, + }, + }); + gop.value_ptr.* = type_index; + } + + const result = gop.value_ptr.*; + return result; + } + fn compilerIntrinsic(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value { const intrinsic_node = analyzer.getScopeNode(scope_index, node_index); const intrinsic_name = analyzer.tokenIdentifier(scope_index, intrinsic_node.token + 1); logln(.sema, .node, "Intrinsic: {s}", .{intrinsic_name}); - const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse panic("Unknown intrinsic: {s}", .{intrinsic_name}); + const intrinsic = data_structures.enumFromString(ValidIntrinsic, intrinsic_name) orelse panic("Unknown intrinsic: {s}", .{intrinsic_name}); const intrinsic_argument_node_list = analyzer.getScopeNodeList(scope_index, analyzer.getScopeNode(scope_index, intrinsic_node.left)); const result = switch (intrinsic) { + .cast => b: { + assert(intrinsic_argument_node_list.items.len == 1); + const value_to_cast_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, intrinsic_argument_node_list.items[0]); + const value_type = analyzer.getValueType(value_to_cast_index); + assert(expect_type != .none); + const cast_result = try analyzer.canCast(expect_type, value_type); + + const value: Value = switch (cast_result) { + .success => .{ + .intrinsic = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .cast = value_to_cast_index, + }, + .type = switch (expect_type) { + .none => unreachable, + .flexible_integer => |flexible_integer| if (flexible_integer.sign) |sign| switch (sign) { + else => unreachable, + } else switch (flexible_integer.byte_count) { + 1 => Type.u8, + 2 => Type.u16, + 4 => Type.u32, + 8 => Type.u64, + else => unreachable, + }, + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }, + }), + }, + .optional_wrap => .{ + .intrinsic = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .optional_wrap = value_to_cast_index, + }, + .type = expect_type.type_index, + }), + }, + else => |t| @panic(@tagName(t)), + }; + break :b value; + }, + .@"error" => { + assert(intrinsic_argument_node_list.items.len == 1); + const message_node = analyzer.getScopeNode(scope_index, intrinsic_argument_node_list.items[0]); + switch (message_node.id) { + .string_literal => panic("error: {s}", .{analyzer.tokenStringLiteral(scope_index, message_node.token)}), + else => |t| @panic(@tagName(t)), + } + unreachable; + }, .import => blk: { assert(intrinsic_argument_node_list.items.len == 1); const import_argument = analyzer.getScopeNode(scope_index, intrinsic_argument_node_list.items[0]); @@ -3607,82 +4260,72 @@ const Analyzer = struct { else => unreachable, } }, - .syscall => blk: { - if (intrinsic_argument_node_list.items.len > 0 and intrinsic_argument_node_list.items.len <= 6 + 1) { - const argument_expect_type = .{ - .flexible_integer = .{ - .byte_count = 8, + .min => blk: { + assert(intrinsic_argument_node_list.items.len == 2); + const value1 = try analyzer.unresolvedAllocate(scope_index, expect_type, intrinsic_argument_node_list.items[0]); + const value2 = try analyzer.unresolvedAllocate(scope_index, expect_type, intrinsic_argument_node_list.items[1]); + const intrinsic_index = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .min = .{ + .left = value1, + .right = value2, }, - }; - const number = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, intrinsic_argument_node_list.items[0]); - assert(!number.invalid); - var arguments = std.mem.zeroes([6]Value.Index); - for (intrinsic_argument_node_list.items[1..], 0..) |argument_node_index, argument_index| { - const argument_value_index = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, argument_node_index); - arguments[argument_index] = argument_value_index; - } - - // TODO: typecheck for usize - // for (arguments[0..intrinsic_argument_node_list.items.len]) |argument| { - // _ = argument; - // } - - break :blk Value{ - .syscall = try analyzer.module.values.syscalls.append(analyzer.allocator, .{ - .number = number, - .arguments = arguments, - .argument_count = @intCast(intrinsic_argument_node_list.items.len - 1), - }), - }; - } else { - unreachable; - } - }, - .@"error" => { - assert(intrinsic_argument_node_list.items.len == 1); - const message_node = analyzer.getScopeNode(scope_index, intrinsic_argument_node_list.items[0]); - switch (message_node.id) { - .string_literal => panic("error: {s}", .{analyzer.tokenStringLiteral(scope_index, message_node.token)}), - else => |t| @panic(@tagName(t)), - } - unreachable; - }, - .cast => blk: { - assert(intrinsic_argument_node_list.items.len == 1); - const value_to_cast_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, intrinsic_argument_node_list.items[0]); - const value_type = analyzer.getValueType(value_to_cast_index); - assert(expect_type != .none); - const cast_result = try analyzer.canCast(expect_type, value_type); - - switch (cast_result) { - inline .success, .cast_to_optional => |cast_type| { - const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ - .value = value_to_cast_index, - .type = switch (expect_type) { - .none => unreachable, - .flexible_integer => |flexible_integer| if (flexible_integer.sign) |sign| switch (sign) { - else => unreachable, - } else switch (flexible_integer.byte_count) { - 1 => Type.u8, - 2 => Type.u16, - 4 => Type.u32, - 8 => Type.u64, - else => unreachable, - }, - .type_index => |type_index| type_index, - else => unreachable, - }, - }); - - break :blk @unionInit(Value, switch (cast_type) { - .success => "cast", - .cast_to_optional => "optional_cast", - else => @compileError("WTF"), - }, cast_index); }, - else => @panic("can't cast"), - } + .type = switch (expect_type) { + .type_index => |type_index| type_index, + .none => analyzer.module.values.array.get(value1).getType(analyzer.module), + else => |t| @panic(@tagName(t)), + }, + }); + break :blk Value{ + .intrinsic = intrinsic_index, + }; }, + .size => blk: { + assert(intrinsic_argument_node_list.items.len == 1); + const intrinsic_argument_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.type, intrinsic_argument_node_list.items[0]); + const type_size = switch (analyzer.module.values.array.get(intrinsic_argument_value_index).*) { + .type => |type_index| analyzer.module.types.array.get(type_index).getSize(), + else => |t| @panic(@tagName(t)), + }; + const intrinsic_argument_value = analyzer.module.values.array.get(intrinsic_argument_value_index); + intrinsic_argument_value.* = .{ + .integer = .{ + .value = type_size, + .type = switch (expect_type) { + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }, + .signedness = .unsigned, + }, + }; + + break :blk intrinsic_argument_value.*; + }, + .syscall => if (intrinsic_argument_node_list.items.len > 0 and intrinsic_argument_node_list.items.len <= 6 + 1) blk: { + const argument_expect_type = .{ + .flexible_integer = .{ + .byte_count = 8, + }, + }; + + var list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, intrinsic_argument_node_list.items.len); + for (intrinsic_argument_node_list.items) |argument_node_index| { + const argument_value_index = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, argument_node_index); + list.appendAssumeCapacity(argument_value_index); + } + + const intrinsic_index = try analyzer.module.values.intrinsics.append(analyzer.allocator, .{ + .kind = .{ + .syscall = list, + }, + .type = Type.usize, + }); + + break :blk Value{ + .intrinsic = intrinsic_index, + }; + } else unreachable, }; return result; diff --git a/bootstrap/frontend/syntactic_analyzer.zig b/bootstrap/frontend/syntactic_analyzer.zig index e986998..1e74553 100644 --- a/bootstrap/frontend/syntactic_analyzer.zig +++ b/bootstrap/frontend/syntactic_analyzer.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const equal = std.mem.eql; +const panic = std.debug.panic; const data_structures = @import("../data_structures.zig"); const ArrayList = data_structures.ArrayList; @@ -30,6 +31,7 @@ pub const Logger = enum { token_errors, symbol_declaration, node_creation, + node_creation_detailed, main_node, container_members, block, @@ -38,11 +40,14 @@ pub const Logger = enum { precedence, @"switch", pointer_like_type_expression, + switch_case, + consume_token, pub var bitset = std.EnumSet(Logger).initMany(&.{ .token_errors, .symbol_declaration, .node_creation, + // .node_creation_detailed, .main_node, .container_members, .block, @@ -51,6 +56,8 @@ pub const Logger = enum { .precedence, .@"switch", .pointer_like_type_expression, + .switch_case, + .consume_token, }); }; @@ -161,7 +168,6 @@ pub const Node = struct { struct_type, container_literal, container_field_initialization, - anonymous_array_element_initialization, array_index_initialization, boolean_not, null_literal, @@ -173,6 +179,7 @@ pub const Node = struct { negation, anonymous_container_literal, anonymous_array_literal, + array_literal, indexed_access, calling_convention, assembly_register, @@ -186,6 +193,11 @@ pub const Node = struct { null_terminated, const_expression, many_pointer_expression, + optional_unwrap, + anonymous_empty_literal, + empty_container_literal_guess, + discarded_assign, + break_expression, }; }; @@ -203,13 +215,14 @@ const Analyzer = struct { file_index: File.Index, allocator: Allocator, node_lists: ArrayList(Node.List) = .{}, + suffix_depth: usize = 0, fn expectToken(analyzer: *Analyzer, token_id: Token.Id) !u32 { const token_i = analyzer.token_i; const token = analyzer.tokens[token_i]; const is_expected_token = token.id == token_id; if (is_expected_token) { - analyzer.token_i += 1; + analyzer.consumeToken(); const result = token_i; return result; } else { @@ -219,6 +232,30 @@ const Analyzer = struct { } } + fn peekTokenAhead(analyzer: *Analyzer, ahead_offset: usize) Token { + return analyzer.tokens[analyzer.token_i + ahead_offset]; + } + + fn peekToken(analyzer: *Analyzer) Token { + return analyzer.peekTokenAhead(0); + } + + fn consumeToken(analyzer: *Analyzer) void { + analyzer.consumeTokens(1); + } + + fn consumeTokens(analyzer: *Analyzer, token_count: u32) void { + assert(analyzer.token_i + token_count <= analyzer.tokens.len); + log(.parser, .consume_token, "Consuming {} {s}: ", .{ token_count, if (token_count == 1) "token" else "tokens" }); + + for (0..token_count) |i| { + const id = analyzer.peekTokenAhead(i).id; + log(.parser, .consume_token, "{s}, ", .{@tagName(id)}); + } + log(.parser, .consume_token, "\n", .{}); + analyzer.token_i += token_count; + } + fn bytes(analyzer: *const Analyzer, token_index: Token.Index) []const u8 { const token = analyzer.tokens[token_index]; return analyzer.source_file[token.start..][0..token.len]; @@ -227,7 +264,7 @@ const Analyzer = struct { fn symbolDeclaration(analyzer: *Analyzer) anyerror!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; + analyzer.consumeToken(); const declaration_name_token = try analyzer.expectToken(.identifier); const declaration_name = analyzer.bytes(declaration_name_token); logln(.parser, .symbol_declaration, "Starting parsing declaration \"{s}\"", .{declaration_name}); @@ -236,7 +273,7 @@ const Analyzer = struct { const type_node_index = switch (analyzer.tokens[analyzer.token_i].id) { .colon => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk try analyzer.typeExpression(); }, else => Node.Index.invalid, @@ -303,7 +340,7 @@ const Analyzer = struct { fn function(analyzer: *Analyzer) !Node.Index { const token = analyzer.token_i; assert(analyzer.tokens[token].id == .fixed_keyword_fn); - analyzer.token_i += 1; + analyzer.consumeToken(); const function_prototype = try analyzer.functionPrototype(); const is_comptime = false; _ = is_comptime; @@ -353,7 +390,7 @@ const Analyzer = struct { .left = Node.Index.invalid, .right = Node.Index.invalid, }); - analyzer.token_i += 1; + analyzer.consumeToken(); break :b result; }, .fixed_keyword_export => b: { @@ -363,11 +400,11 @@ const Analyzer = struct { .left = Node.Index.invalid, .right = Node.Index.invalid, }); - analyzer.token_i += 1; + analyzer.consumeToken(); break :b result; }, .fixed_keyword_cc => b: { - analyzer.token_i += 1; + analyzer.consumeToken(); _ = try analyzer.expectToken(.left_parenthesis); const calling_conv_expression = try analyzer.expression(); _ = try analyzer.expectToken(.right_parenthesis); @@ -407,12 +444,9 @@ const Analyzer = struct { const identifier = try analyzer.expectToken(.identifier); _ = try analyzer.expectToken(.colon); const type_expression = try analyzer.typeExpression(); - // const type_expression_node = analyzer.nodes.items[type_expression.unwrap()]; - // _ = type_expression_node; - // logln("Type expression node: {}", .{type_expression_node}); if (analyzer.tokens[analyzer.token_i].id == .comma) { - analyzer.token_i += 1; + analyzer.consumeToken(); } try list.append(analyzer.allocator, try analyzer.addNode(.{ @@ -469,6 +503,11 @@ const Analyzer = struct { _ = try analyzer.expectToken(.semicolon); break :blk intrinsic; }, + .fixed_keyword_break => b: { + const node_index = try analyzer.breakExpression(); + _ = try analyzer.expectToken(.semicolon); + break :b node_index; + }, else => |t| @panic(@tagName(t)), }; @@ -512,7 +551,7 @@ const Analyzer = struct { fn switchExpression(analyzer: *Analyzer) anyerror!Node.Index { logln(.parser, .@"switch", "Parsing switch...", .{}); const switch_token = analyzer.token_i; - analyzer.token_i += 1; + analyzer.consumeToken(); _ = try analyzer.expectToken(.left_parenthesis); const switch_expression = try analyzer.expression(); _ = try analyzer.expectToken(.right_parenthesis); @@ -526,7 +565,7 @@ const Analyzer = struct { logln(.parser, .@"switch", "Parsing switch case...", .{}); const case_node = switch (analyzer.tokens[case_token].id) { .fixed_keyword_else => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk Node.Index.invalid; }, else => blk: { @@ -534,7 +573,7 @@ const Analyzer = struct { while (true) { try array_list.append(analyzer.allocator, try analyzer.expression()); switch (analyzer.tokens[analyzer.token_i].id) { - .comma => analyzer.token_i += 1, + .comma => analyzer.consumeToken(), .equal => switch (analyzer.tokens[analyzer.token_i + 1].id) { .greater => break, else => {}, @@ -560,6 +599,7 @@ const Analyzer = struct { false => try analyzer.assignExpression(), }; + logln(.parser, .switch_case, "Comma token: \n```\n{s}\n```\n", .{analyzer.source_file[analyzer.tokens[analyzer.token_i].start..]}); _ = try analyzer.expectToken(.comma); const node = try analyzer.addNode(.{ @@ -584,14 +624,14 @@ const Analyzer = struct { fn ifExpression(analyzer: *Analyzer) anyerror!Node.Index { const if_token = analyzer.token_i; - analyzer.token_i += 1; + analyzer.consumeToken(); _ = try analyzer.expectToken(.left_parenthesis); const if_condition = try analyzer.expression(); _ = try analyzer.expectToken(.right_parenthesis); const payload = if (analyzer.tokens[analyzer.token_i].id == .vertical_bar) blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); const payload_node = switch (analyzer.tokens[analyzer.token_i].id) { .identifier => try analyzer.identifierNode(), .discard => try analyzer.discardNode(), @@ -612,7 +652,7 @@ const Analyzer = struct { const result = switch (analyzer.tokens[analyzer.token_i].id) { .fixed_keyword_else => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk try analyzer.addNode(.{ .id = .if_else, @@ -651,7 +691,7 @@ const Analyzer = struct { const for_expression = switch (analyzer.tokens[analyzer.token_i].id) { .period => switch (analyzer.tokens[analyzer.token_i + 1].id) { .period => blk: { - analyzer.token_i += 2; + analyzer.consumeTokens(2); const second = try analyzer.expression(); break :blk ForExpression{ @@ -682,11 +722,11 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), }; - analyzer.token_i += 1; + analyzer.consumeToken(); switch (analyzer.tokens[analyzer.token_i].id) { .vertical_bar => {}, - .comma => analyzer.token_i += 1, + .comma => analyzer.consumeToken(), else => |t| @panic(@tagName(t)), } @@ -732,6 +772,17 @@ const Analyzer = struct { return for_node; } + fn breakExpression(analyzer: *Analyzer) !Node.Index { + const t = try analyzer.expectToken(.fixed_keyword_break); + const node_index = try analyzer.addNode(.{ + .id = .break_expression, + .token = t, + .left = Node.Index.invalid, + .right = Node.Index.invalid, + }); + return node_index; + } + fn assignExpression(analyzer: *Analyzer) !Node.Index { const left = try analyzer.expression(); const expression_token = analyzer.token_i; @@ -740,7 +791,7 @@ const Analyzer = struct { .equal => .assign, .plus => switch (analyzer.tokens[analyzer.token_i + 1].id) { .equal => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk .add_assign; }, else => |t| @panic(@tagName(t)), @@ -748,7 +799,7 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), }; - analyzer.token_i += 1; + analyzer.consumeToken(); const right = try analyzer.expression(); @@ -769,14 +820,14 @@ const Analyzer = struct { .identifier => try analyzer.addNode(.{ .id = .assembly_register, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token; }, .left = Node.Index.invalid, .right = Node.Index.invalid, }), .number_literal => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk analyzer.addNode(.{ .id = .number_literal, .token = token, @@ -785,7 +836,7 @@ const Analyzer = struct { }); }, .left_brace => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); const result = try analyzer.expression(); _ = try analyzer.expectToken(.right_brace); break :blk result; @@ -812,7 +863,7 @@ const Analyzer = struct { const asm_operand = try analyzer.parseAsmOperand(); switch (analyzer.tokens[analyzer.token_i].id) { .semicolon => {}, - .comma => analyzer.token_i += 1, + .comma => analyzer.consumeToken(), else => |t| @panic(@tagName(t)), } try operand_list.append(analyzer.allocator, asm_operand); @@ -843,14 +894,14 @@ const Analyzer = struct { try list.append(analyzer.allocator, parameter); switch (analyzer.tokens[analyzer.token_i].id) { - .comma => analyzer.token_i += 1, + .comma => analyzer.consumeToken(), .right_parenthesis => continue, else => |t| @panic(@tagName(t)), } } // Consume the right parenthesis - analyzer.token_i += 1; + analyzer.consumeToken(); return try analyzer.addNode(.{ .id = .compiler_intrinsic, @@ -938,10 +989,11 @@ const Analyzer = struct { }); fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index { + assert(minimum_precedence >= 0); var result = try analyzer.prefixExpression(); if (!result.invalid) { const prefix_node = analyzer.nodes.items[result.unwrap()]; - logln(.parser, .precedence, "Prefix: {}", .{prefix_node.id}); + logln(.parser, .precedence, "Prefix: {s}", .{@tagName(prefix_node.id)}); } var banned_precedence: i32 = -1; @@ -960,6 +1012,8 @@ const Analyzer = struct { .fixed_keyword_return, .identifier, .colon, + .fixed_keyword_if, + .discard, => break, else => blk: { const next_token_index = analyzer.token_i + 1; @@ -1029,6 +1083,7 @@ const Analyzer = struct { } }, }; + logln(.parser, .precedence, "Precedence operator: {s}", .{@tagName(operator)}); const precedence = operator_precedence.get(operator); if (precedence < minimum_precedence) { @@ -1062,9 +1117,10 @@ const Analyzer = struct { => 1, // else => |t| @panic(@tagName(t)), }; - analyzer.token_i += @as(u32, 1) + extra_tokens; + analyzer.consumeTokens(@as(u32, 1) + extra_tokens); // TODO: fix this + logln(.parser, .precedence, "Going for right in expressionPrecedence with operator {s}", .{@tagName(operator)}); const right = try analyzer.expressionPrecedence(precedence + 1); const node_id = operator_node_id.get(operator); @@ -1105,7 +1161,7 @@ const Analyzer = struct { return try analyzer.addNode(.{ .id = node_id, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token; }, .left = try analyzer.prefixExpression(), @@ -1131,6 +1187,8 @@ const Analyzer = struct { .left_parenthesis, .keyword_signed_integer, .keyword_unsigned_integer, + .fixed_keyword_ssize, + .fixed_keyword_usize, .fixed_keyword_enum, .fixed_keyword_struct, .discard, @@ -1142,7 +1200,7 @@ const Analyzer = struct { .id = .@"return", .token = blk: { const token = analyzer.token_i; - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token; }, .left = try analyzer.expression(), @@ -1150,6 +1208,7 @@ const Analyzer = struct { }), // todo:? .left_brace => try analyzer.block(.{ .is_comptime = false }), + .fixed_keyword_if => try analyzer.ifExpression(), else => |id| std.debug.panic("WARN: By default, calling curlySuffixExpression with {s}", .{@tagName(id)}), }; @@ -1160,12 +1219,7 @@ const Analyzer = struct { const left = try analyzer.typeExpression(); return switch (analyzer.tokens[analyzer.token_i].id) { - .left_brace => try analyzer.addNode(.{ - .id = .container_literal, - .token = analyzer.token_i, - .left = left, - .right = try analyzer.fieldInitialization(), - }), + .left_brace => try analyzer.containerLiteral(left), else => left, }; } @@ -1173,7 +1227,7 @@ const Analyzer = struct { fn noReturn(analyzer: *Analyzer) !Node.Index { const token_i = analyzer.token_i; assert(analyzer.tokens[token_i].id == .fixed_keyword_noreturn); - analyzer.token_i += 1; + analyzer.consumeToken(); return try analyzer.addNode(.{ .id = .keyword_noreturn, .token = token_i, @@ -1184,7 +1238,7 @@ const Analyzer = struct { fn boolLiteral(analyzer: *Analyzer) !Node.Index { const token_i = analyzer.token_i; - analyzer.token_i += 1; + analyzer.consumeToken(); return try analyzer.addNode(.{ .id = switch (analyzer.tokens[token_i].id) { .fixed_keyword_true => .keyword_true, @@ -1224,7 +1278,7 @@ const Analyzer = struct { .left = Node.Index.invalid, .right = Node.Index.invalid, }); - analyzer.token_i += 1; + analyzer.consumeToken(); return termination_node_index; } @@ -1237,7 +1291,7 @@ const Analyzer = struct { const expression_type: Node.Id = switch (expected) { .single_pointer_type => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk .pointer_type; }, @@ -1263,10 +1317,14 @@ const Analyzer = struct { _ = try analyzer.expectToken(.left_bracket); switch (analyzer.tokens[analyzer.token_i].id) { .right_bracket => { - analyzer.token_i += 1; + analyzer.consumeToken(); + break :blk .slice_type; + }, + .colon => { + try list.append(analyzer.allocator, try analyzer.parseTermination()); + _ = try analyzer.expectToken(.right_bracket); break :blk .slice_type; }, - .colon => unreachable, else => { const length_expression = try analyzer.expression(); try list.append(analyzer.allocator, length_expression); @@ -1295,7 +1353,7 @@ const Analyzer = struct { }), else => Node.Index.invalid, }; - analyzer.token_i += @intFromBool(analyzer.tokens[analyzer.token_i].id == .fixed_keyword_const); + analyzer.consumeTokens(@intFromBool(analyzer.tokens[analyzer.token_i].id == .fixed_keyword_const)); if (!const_node.invalid) { try list.append(analyzer.allocator, const_node); @@ -1344,7 +1402,7 @@ const Analyzer = struct { return switch (analyzer.tokens[first].id) { else => try analyzer.errorUnionExpression(), .question_mark => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk try analyzer.addNode(.{ .id = .optional_type, .token = first, @@ -1375,6 +1433,8 @@ const Analyzer = struct { } fn suffixExpression(analyzer: *Analyzer) !Node.Index { + analyzer.suffix_depth += 1; + defer analyzer.suffix_depth -= 1; var result = try analyzer.primaryTypeExpression(); while (true) { @@ -1384,16 +1444,18 @@ const Analyzer = struct { } else { if (analyzer.tokens[analyzer.token_i].id == .left_parenthesis) { const left_parenthesis = analyzer.token_i; - analyzer.token_i += 1; + analyzer.consumeToken(); var expression_list = ArrayList(Node.Index){}; + logln(.parser, .suffix, "[DEPTH={}] Initializating suffix call-like expression", .{analyzer.suffix_depth}); while (analyzer.tokens[analyzer.token_i].id != .right_parenthesis) { const current_token = analyzer.token_i; + logln(.parser, .suffix, "[DEPTH={}] First token: {s}", .{ analyzer.suffix_depth, @tagName(analyzer.tokens[current_token].id) }); var parameter = try analyzer.expression(); const parameter_node = analyzer.nodes.items[parameter.unwrap()]; - logln(.parser, .suffix, "Paremeter node: {s}", .{@tagName(parameter_node.id)}); + logln(.parser, .suffix, "[DEPTH={}] Parameter node: {s}", .{ analyzer.suffix_depth, @tagName(parameter_node.id) }); if (analyzer.tokens[analyzer.token_i].id == .equal) { - analyzer.token_i += 1; + analyzer.consumeToken(); parameter = try analyzer.addNode(.{ .id = .named_argument, @@ -1404,16 +1466,19 @@ const Analyzer = struct { } try expression_list.append(analyzer.allocator, parameter); switch (analyzer.tokens[analyzer.token_i].id) { - .comma => analyzer.token_i += 1, + .comma => analyzer.consumeToken(), .right_parenthesis => {}, .colon, .right_brace, .right_bracket => unreachable, + .period => panic("[DEPTH={}] Unexpected period", .{analyzer.suffix_depth}), else => |t| @panic(@tagName(t)), } } + logln(.parser, .suffix, "[DEPTH={}] Ending suffix call-like expression", .{analyzer.suffix_depth}); + _ = try analyzer.expectToken(.right_parenthesis); // const is_comma = analyzer.tokens[analyzer.token_i].id == .comma; - return try analyzer.addNode(.{ + result = try analyzer.addNode(.{ .id = .call, .token = left_parenthesis, .left = result, @@ -1428,14 +1493,18 @@ const Analyzer = struct { unreachable; } - fn fieldInitialization(analyzer: *Analyzer) !Node.Index { - _ = try analyzer.expectToken(.left_brace); + fn containerLiteral(analyzer: *Analyzer, type_node: Node.Index) anyerror!Node.Index { + const token = try analyzer.expectToken(.left_brace); var list = ArrayList(Node.Index){}; + const InitializationType = enum { anonymous, array_indices, container_field_names, + empty_literal, + empty_container_literal_guess, + empty_array_literal, }; var current_initialization: ?InitializationType = null; @@ -1443,41 +1512,44 @@ const Analyzer = struct { while (analyzer.tokens[analyzer.token_i].id != .right_brace) { const start_token = analyzer.token_i; const iteration_initialization_type: InitializationType = switch (analyzer.tokens[start_token].id) { - .period => blk: { - analyzer.token_i += 1; - _ = try analyzer.expectToken(.identifier); - _ = try analyzer.expectToken(.equal); - const field_expression_initializer = try analyzer.expression(); + .period => switch (analyzer.tokens[analyzer.token_i + 1].id) { + .identifier => switch (analyzer.tokens[analyzer.token_i + 2].id) { + .equal => blk: { + analyzer.consumeTokens(3); + const field_expression_initializer = try analyzer.expression(); - const field_initialization = try analyzer.addNode(.{ - .id = .container_field_initialization, - .token = start_token, - .left = field_expression_initializer, - .right = Node.Index.invalid, - }); + const field_initialization = try analyzer.addNode(.{ + .id = .container_field_initialization, + .token = start_token, + .left = field_expression_initializer, + .right = Node.Index.invalid, + }); - try list.append(analyzer.allocator, field_initialization); - _ = try analyzer.expectToken(.comma); + try list.append(analyzer.allocator, field_initialization); + _ = try analyzer.expectToken(.comma); - break :blk .container_field_names; + break :blk .container_field_names; + }, + else => |t| @panic(@tagName(t)), + }, + else => blk: { + try list.append(analyzer.allocator, try analyzer.anonymousExpression()); + _ = try analyzer.expectToken(.comma); + break :blk .anonymous; + }, }, .string_literal, .identifier, + .number_literal, + .hash, => blk: { const field_expression_initializer = try analyzer.expression(); switch (analyzer.tokens[analyzer.token_i].id) { - .comma => analyzer.token_i += 1, + .comma => analyzer.consumeToken(), else => {}, } - const field_initialization = try analyzer.addNode(.{ - .id = .anonymous_array_element_initialization, - .token = start_token, - .left = field_expression_initializer, - .right = Node.Index.invalid, - }); - - try list.append(analyzer.allocator, field_initialization); + try list.append(analyzer.allocator, field_expression_initializer); break :blk .anonymous; }, else => |t| @panic(@tagName(t)), @@ -1494,41 +1566,41 @@ const Analyzer = struct { _ = try analyzer.expectToken(.right_brace); - return try analyzer.nodeList(list); - } - - fn arrayInitialization(analyzer: *Analyzer) !Node.Index { - _ = try analyzer.expectToken(.left_bracket); - var list = ArrayList(Node.Index){}; - while (analyzer.tokens[analyzer.token_i].id != .right_bracket) { - const node_index: Node.Index = switch (analyzer.tokens[analyzer.token_i].id) { - .left_bracket => @panic("Left bracket"), - else => Node.Index.invalid, - }; - - const initialization_node = try analyzer.expression(); - const node = switch (node_index.invalid) { - true => initialization_node, - false => @panic("left bracket"), - }; - - try list.append(analyzer.allocator, node); - - switch (analyzer.tokens[analyzer.token_i].id) { - .comma => analyzer.token_i += 1, - .right_bracket => {}, + const initialization: InitializationType = current_initialization orelse switch (type_node.invalid) { + true => .empty_literal, + false => switch (analyzer.nodes.items[type_node.unwrap()].id) { + .identifier => .empty_container_literal_guess, + .array_type => .empty_array_literal, else => |t| @panic(@tagName(t)), - } - } - _ = try analyzer.expectToken(.right_bracket); + }, + }; - return try analyzer.nodeList(list); + return try analyzer.addNode(.{ + .id = switch (type_node.invalid) { + true => switch (initialization) { + .container_field_names => .anonymous_container_literal, + .empty_literal => .anonymous_empty_literal, + .anonymous => .anonymous_array_literal, + else => |t| @panic(@tagName(t)), + }, + false => switch (initialization) { + .container_field_names => .container_literal, + .empty_container_literal_guess => .empty_container_literal_guess, + .anonymous => .array_literal, + .empty_array_literal => .array_literal, + else => |t| @panic(@tagName(t)), + }, + }, + .token = token, + .left = type_node, + .right = try analyzer.nodeList(list), + }); } fn discardNode(analyzer: *Analyzer) !Node.Index { const token = analyzer.token_i; assert(analyzer.tokens[token].id == .discard); - analyzer.token_i += 1; + analyzer.consumeToken(); return try analyzer.addNode(.{ .id = .discard, .token = token, @@ -1543,11 +1615,11 @@ const Analyzer = struct { return try switch (token.id) { .fixed_keyword_fn => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk analyzer.functionPrototype(); }, .string_literal => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk analyzer.addNode(.{ .id = .string_literal, .token = token_i, @@ -1556,7 +1628,7 @@ const Analyzer = struct { }); }, .number_literal => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk analyzer.addNode(.{ .id = .number_literal, .token = token_i, @@ -1571,7 +1643,7 @@ const Analyzer = struct { .fixed_keyword_undefined => analyzer.addNode(.{ .id = .undefined, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, .left = Node.Index.invalid, @@ -1580,7 +1652,7 @@ const Analyzer = struct { .fixed_keyword_null => analyzer.addNode(.{ .id = .null_literal, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, .left = Node.Index.invalid, @@ -1589,7 +1661,7 @@ const Analyzer = struct { .fixed_keyword_unreachable => analyzer.addNode(.{ .id = .@"unreachable", .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, .left = Node.Index.invalid, @@ -1599,7 +1671,7 @@ const Analyzer = struct { .fixed_keyword_bool => analyzer.addNode(.{ .id = .bool_type, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, .left = Node.Index.invalid, @@ -1612,10 +1684,14 @@ const Analyzer = struct { else => unreachable, }, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, - .left = @bitCast(@as(u32, try std.fmt.parseInt(u16, analyzer.bytes(token_i)[1..], 10))), + .left = @bitCast(@as(u32, try std.fmt.parseInt(u16, b: { + const slice = analyzer.bytes(token_i)[1..]; + if (slice.len == 0) unreachable; + break :b slice; + }, 10))), .right = Node.Index.invalid, }), .fixed_keyword_usize, .fixed_keyword_ssize => |size_type| analyzer.addNode(.{ @@ -1625,7 +1701,7 @@ const Analyzer = struct { else => unreachable, }, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, .left = Node.Index.invalid, @@ -1634,51 +1710,22 @@ const Analyzer = struct { .fixed_keyword_void => analyzer.addNode(.{ .id = .void_type, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token_i; }, .left = Node.Index.invalid, .right = Node.Index.invalid, }), .fixed_keyword_switch => try analyzer.switchExpression(), - .period => switch (analyzer.tokens[token_i + 1].id) { - .identifier => try analyzer.addNode(.{ - .id = .enum_literal, - .token = blk: { - analyzer.token_i += 2; - break :blk token_i; - }, - .left = Node.Index.invalid, - .right = Node.Index.invalid, - }), - .left_brace => try analyzer.addNode(.{ - .id = .anonymous_container_literal, - .token = blk: { - analyzer.token_i += 1; - break :blk token_i; - }, - .left = try analyzer.fieldInitialization(), - .right = Node.Index.invalid, - }), - .left_bracket => try analyzer.addNode(.{ - .id = .anonymous_array_literal, - .token = blk: { - analyzer.token_i += 1; - break :blk token_i; - }, - .left = try analyzer.arrayInitialization(), - .right = Node.Index.invalid, - }), - else => |t| @panic(@tagName(t)), - }, + .period => try analyzer.anonymousExpression(), .fixed_keyword_enum => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); // TODO: is this the best way? if (analyzer.tokens[analyzer.token_i].id == .left_parenthesis) { - analyzer.token_i += 1; + analyzer.consumeToken(); assert(analyzer.tokens[analyzer.token_i + 1].id == .right_parenthesis); - analyzer.token_i += 2; + analyzer.consumeTokens(2); } _ = try analyzer.expectToken(.left_brace); @@ -1693,13 +1740,13 @@ const Analyzer = struct { }); }, .fixed_keyword_struct => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); // TODO: is this the best way? if (analyzer.tokens[analyzer.token_i].id == .left_parenthesis) { - analyzer.token_i += 1; + analyzer.consumeToken(); assert(analyzer.tokens[analyzer.token_i + 1].id == .right_parenthesis); - analyzer.token_i += 2; + analyzer.consumeTokens(2); } _ = try analyzer.expectToken(.left_brace); @@ -1714,7 +1761,7 @@ const Analyzer = struct { }); }, .left_parenthesis => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); const expr = try analyzer.expression(); _ = try analyzer.expectToken(.right_parenthesis); break :blk try analyzer.addNode(.{ @@ -1731,16 +1778,34 @@ const Analyzer = struct { }; } + fn anonymousExpression(analyzer: *Analyzer) !Node.Index { + const token_i = analyzer.token_i; + _ = try analyzer.expectToken(.period); + return switch (analyzer.tokens[analyzer.token_i].id) { + .identifier => try analyzer.addNode(.{ + .id = .enum_literal, + .token = blk: { + analyzer.consumeToken(); + break :blk token_i; + }, + .left = Node.Index.invalid, + .right = Node.Index.invalid, + }), + .left_brace => try analyzer.containerLiteral(Node.Index.invalid), + else => |t| @panic(@tagName(t)), + }; + } + // TODO: fn suffixOperator(analyzer: *Analyzer, left: Node.Index) !Node.Index { const token = analyzer.token_i; - return switch (analyzer.tokens[token].id) { + const result: Node.Index = switch (analyzer.tokens[token].id) { .left_bracket => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); const index_expression = try analyzer.expression(); if (analyzer.tokens[analyzer.token_i].id == .period and analyzer.token_i + 1 < analyzer.tokens.len and analyzer.tokens[analyzer.token_i + 1].id == .period) { - analyzer.token_i += 2; + analyzer.consumeTokens(2); const range_end_expression = switch (analyzer.tokens[analyzer.token_i].id) { .right_bracket => Node.Index.invalid, else => try analyzer.expression(), @@ -1773,14 +1838,14 @@ const Analyzer = struct { .identifier => try analyzer.addNode(.{ .id = .field_access, .token = blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :blk token; }, .left = left, .right = blk: { //TODO ??? const result: Node.Index = @bitCast(analyzer.token_i); - analyzer.token_i += 1; + analyzer.consumeToken(); logln(.parser, .suffix, "WARNING: rhs has node index {} but it's token #{}", .{ result, token }); break :blk result; }, @@ -1789,7 +1854,7 @@ const Analyzer = struct { .ampersand => try analyzer.addNode(.{ .id = .address_of, .token = blk: { - analyzer.token_i += 2; + analyzer.consumeTokens(2); break :blk token; }, .left = left, @@ -1798,16 +1863,33 @@ const Analyzer = struct { .at => try analyzer.addNode(.{ .id = .pointer_dereference, .token = blk: { - analyzer.token_i += 2; + analyzer.consumeTokens(2); break :blk token; }, .left = left, .right = Node.Index.invalid, }), + .question_mark => try analyzer.addNode(.{ + .id = .optional_unwrap, + .token = blk: { + analyzer.consumeToken(); + break :blk token; + }, + .left = left, + .right = .{ + .value = @intCast(blk: { + const t = analyzer.token_i; + analyzer.consumeToken(); + break :blk t; + }), + }, + }), else => |t| @panic(@tagName(t)), }, else => Node.Index.invalid, }; + + return result; } fn addNode(analyzer: *Analyzer, node: Node) !Node.Index { @@ -1817,6 +1899,14 @@ const Analyzer = struct { // @breakpoint(); // } logln(.parser, .node_creation, "Adding node #{} (0x{x}) {s} to file #{}", .{ index, @intFromPtr(&analyzer.nodes.items[index]), @tagName(node.id), analyzer.file_index.uniqueInteger() }); + if (Logger.bitset.contains(.node_creation_detailed)) { + const chunk_start = analyzer.tokens[node.token].start; + const chunk_from_start = analyzer.source_file[chunk_start..]; + const chunk_end = @min(200, chunk_from_start.len); + const chunk = chunk_from_start[0..chunk_end]; + logln(.parser, .node_creation, "[SOURCE]: ```\n{s}\n```\n", .{chunk}); + } + // if (node.id == .identifier) { // logln("Node identifier: {s}", .{analyzer.bytes(node.token)}); // } @@ -1839,7 +1929,7 @@ const Analyzer = struct { fn identifierNode(analyzer: *Analyzer) !Node.Index { const identifier_token = analyzer.token_i; assert(analyzer.tokens[identifier_token].id == .identifier); - analyzer.token_i += 1; + analyzer.consumeToken(); return try analyzer.addNode(.{ .id = .identifier, .token = identifier_token, @@ -1857,7 +1947,7 @@ const Analyzer = struct { 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; + analyzer.consumeToken(); const comptime_block = try analyzer.block(.{ .is_comptime = true }); break :blk try analyzer.addNode(.{ @@ -1870,7 +1960,7 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), }, .identifier => blk: { - analyzer.token_i += 1; + analyzer.consumeToken(); switch (container_type) { .@"struct" => { _ = try analyzer.expectToken(.colon); @@ -1878,11 +1968,10 @@ const Analyzer = struct { const field_type = try analyzer.typeExpression(); const field_default_node = if (analyzer.tokens[analyzer.token_i].id == .equal) b: { - analyzer.token_i += 1; + analyzer.consumeToken(); const default_index = try analyzer.expression(); const default_node = analyzer.nodes.items[default_index.unwrap()]; - _ = default_node; - assert(.id != .node_list); + assert(default_node.id != .node_list); break :b default_index; } else Node.Index.invalid; @@ -1901,7 +1990,7 @@ const Analyzer = struct { const value_associated = switch (analyzer.tokens[analyzer.token_i].id) { .comma => Node.Index.invalid, else => value: { - analyzer.token_i += 1; + analyzer.consumeToken(); break :value try analyzer.expression(); }, }; @@ -1974,24 +2063,6 @@ pub fn analyze(allocator: Allocator, tokens: []const Token, source_file: []const }; } -const ExpressionMutabilityQualifier = enum { - @"const", - @"var", -}; - -const Keyword = enum { - @"return", - @"fn", - @"while", - void, - noreturn, -}; - -// These types are meant to be used by the semantic analyzer -pub const ContainerDeclaration = struct { - members: []const Node.Index, -}; - pub const SymbolDeclaration = struct { type_node: Node.Index, initialization_node: Node.Index, diff --git a/ci.sh b/ci.sh index c323f60..8d7f7a7 100755 --- a/ci.sh +++ b/ci.sh @@ -54,7 +54,6 @@ echo -e "\e[35m[SUMMARY]\e[0m" echo -e "\e[35m=========\e[0m" echo -e "Ran $total_test_count compilations (\e[32m$passed_compilation_count\e[0m succeeded, \e[31m$failed_compilation_count\e[0m failed)." echo -e "Ran $ran_test_count tests (\e[32m $passed_test_count\e[0m passed, \e[31m$failed_test_count\e[0m failed)." -echo -e "\e[35m=========\e[0m" if [[ "$failed_compilation_count" != "0" ]]; then printf $"\nFailed compilations:\n" @@ -74,6 +73,8 @@ if [[ "$failed_test_count" != "0" ]]; then done fi +echo -e "\e[35m=========\e[0m" + if [[ "$failed_test_count" == "0" && "$failed_compilation_count" == "0" ]]; then echo -e "\e[32mSUCCESS!\e[0m" true diff --git a/lib/std/build.nat b/lib/std/build.nat index cd25921..119c5f5 100644 --- a/lib/std/build.nat +++ b/lib/std/build.nat @@ -1,7 +1,47 @@ const std = #import("std"); +const assert = std.assert; +const Allocator = std.Allocator; const Target = std.Target; const Executable = struct{ target: Target, - main_source_path: []const u8, + main_source_path: [:0]const u8, + + const compile = fn(executable: Executable, compiler_path: [&:0]const u8) bool { + if (std.os.duplicate_process()) |pid| { + if (pid == 0) { + const argv = [_:null] ?[&:0]const u8{ compiler_path, #cast(executable.main_source_path.ptr), }; + std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values); + return true; + } else { + if (std.os.waitpid(pid, flags = 0)) |raw_status| { + if (std.os.ifexited(status = raw_status)) { + const exit_status = std.os.exitstatus(status = raw_status); + + if (exit_status == 0) { + return true; + } else { + std.print(bytes = "Bad exit code\n"); + return false; + } + } else if (std.os.ifsignaled(status = raw_status)) { + std.print(bytes = "Signaled\n"); + return false; + } else if (std.os.ifstopped(status = raw_status)) { + std.print(bytes = "Stopped\n"); + return false; + } else { + std.print(bytes = "Unknown process termination\n"); + return false; + } + } else { + std.print(bytes = "Wait failed\n"); + return false; + } + } + } else { + std.print(bytes = "Unable to create child process\n"); + return false; + } + } }; diff --git a/lib/std/os.nat b/lib/std/os.nat index ad87e2c..eec940c 100644 --- a/lib/std/os.nat +++ b/lib/std/os.nat @@ -7,10 +7,9 @@ const system = switch (current) { .windows => windows, }; - const exit = fn(exit_code: s32) noreturn { switch (current) { - .linux => _ = #syscall(231, exit_code), + .linux => _ = #syscall(#cast(linux.Syscall.exit_group), exit_code), .macos => macos.exit(exit_code), .windows => windows.ExitProcess(exit_code), } @@ -18,7 +17,64 @@ const exit = fn(exit_code: s32) noreturn { unreachable; } -const ProcessId = system.ProcessId; +const max_file_operation_byte_count = switch (current) { + .linux => 0x7ffff000, + else => #error("OS not supported"), +}; + +const FileDescriptor = struct{ + handle: system.FileDescriptor, + + const read = fn(file_descriptor: FileDescriptor, bytes: []u8) ?usize { + if (bytes.len > 0) { + switch (current) { + .linux => { + const len: usize = #min(max_file_operation_byte_count, bytes.len); + if (linux.unwrapSyscall(syscall_result = #syscall(#cast(linux.Syscall.read), file_descriptor.handle, #cast(bytes.ptr), len))) |byte_count| { + return byte_count; + } else { + return null; + } + }, + else => #error("OS not supported"), + } + } else { + return 0; + } + } + + const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) ?usize { + switch (current) { + .linux => { + const len: usize = #min(max_file_operation_byte_count, bytes.len); + const raw_result = #syscall(#cast(linux.Syscall.write), file_descriptor.handle, #cast(bytes.ptr), len); + if (linux.unwrapSyscall(syscall_result = raw_result)) |byte_count| { + return byte_count; + } else { + return null; + } + }, + else => #error("OS not supported"), + } + } +}; + +const StdFileDescriptor = enum { + stdin = 0, + stdout = 1, + stderr = 2, + + const get = fn(descriptor: StdFileDescriptor) FileDescriptor{ + switch (current) { + .linux, .macos => { + return FileDescriptor{ + .handle = #cast(descriptor), + }; + }, + else => #error("OS not supported"), + } + } +}; const ProtectionFlags = struct(u32){ read: bool, @@ -83,16 +139,11 @@ const max_path_byte_count = switch (current) { else => #error("OS not supported"), }; -const current_executable_path = fn(allocator: &Allocator) ?[]u8 { +const current_executable_path = fn(buffer: []u8) ?[]u8 { switch (current) { .linux => { - var buffer: [max_path_byte_count]u8 = undefined; - if (readlink(file_path = "/proc/self/exe", buffer = buffer.&)) |bytes| { - if (allocator.duplicate_bytes(bytes)) |result| { - return result; - } else { - return null; - } + if (readlink(file_path = "/proc/self/exe", buffer)) |bytes| { + return bytes; } else { return null; } @@ -101,7 +152,11 @@ const current_executable_path = fn(allocator: &Allocator) ?[]u8 { } } -const duplicate_process = fn () ?ProcessId { +const Process = struct{ + const Id = system.ProcessId; +}; + +const duplicate_process = fn () ?Process.Id { switch (current) { .linux => { if (linux.unwrapSyscall(syscall_result = linux.fork())) |fork_result| { @@ -123,41 +178,186 @@ const execute = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: } } -const FileDescriptor = struct{ - handle: system.FileDescriptor, - - const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) ?usize { - switch (current) { - .linux => { - const raw_result = #syscall(1, file_descriptor.handle, #cast(bytes.ptr), bytes.len); - if (linux.unwrapSyscall(syscall_result = raw_result)) |byte_count| { - return byte_count; - } else { - return null; - } - }, - else => #error("OS not supported"), - } +const event_file_descriptor = fn(initial_value: u32, flags: u32) ?s32 { + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.event_file_descriptor(count = initial_value, flags))) |raw_result| { + const result: s32 = #cast(raw_result); + return result; + } else { + return null; + } + }, + else => #error("OS not supported"), } -}; +} -const StdFileDescriptor = enum { - stdin = 0, - stdout = 1, - stderr = 2, +const dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) bool { + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.dup2(old = old_file_descriptor, new = new_file_descriptor))) |_| { + return true; + } else { + return false; + } + }, + else => #error("OS not supported"), + } +} - const get = fn(descriptor: StdFileDescriptor) FileDescriptor{ - switch (current) { - .linux, .macos => { - return FileDescriptor{ - .handle = #cast(descriptor), +const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) ?FileDescriptor{ + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.open(path, flags, permissions))) |raw_result| { + const file_descriptor = FileDescriptor{ + .handle = #cast(raw_result), }; - }, - else => #error("OS not supported"), - } - } -}; + return file_descriptor; + } else { + return null; + } + }, + else => #error("OS not supported"), + } +} + +const close = fn(file_descriptor: s32) bool { + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.close(file_descriptor))) |_| { + return true; + } else { + return false; + } + }, + else => #error("OS not supported"), + } +} + +const pipe2 = fn(flags: u32) ?[2]system.FileDescriptor{ + switch (current) { + .linux => { + var pipe: [2]s32 = undefined; + if (linux.unwrapSyscall(syscall_result = linux.pipe2(pipe_pointer = pipe.&, flags))) |_| { + return pipe; + } else { + return null; + } + }, + else => #error("OS not supported"), + } +} + +const set_up_child_process_io_posix = fn(io_channel_behavior: IoChannelBehavior, pipe_file_descriptor: s32, std_io_channel_descriptor: s32, dev_null_file_descriptor: s32) bool { + switch (io_channel_behavior) { + .pipe => return dup2(old_file_descriptor = pipe_file_descriptor, new_file_descriptor = std_io_channel_descriptor), + .close => { + if (!close(file_descriptor = std_io_channel_descriptor)) { + unreachable; + } + return true; + }, + .inherit => return true, + .ignore => return dup2(old_file_descriptor = dev_null_file_descriptor, new_file_descriptor = std_io_channel_descriptor), + } +} + +const PollFileDescriptor = system.PollFileDescriptor; + +const poll = fn(file_descriptors: []PollFileDescriptor, timeout: s32) ?usize { + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.poll(file_descriptors = file_descriptors.ptr, file_descriptor_count = file_descriptors.len, timeout = timeout))) |result| { + return result; + } else { + return null; + } + }, + else => #error("OS not supported"), + } +} + +const write_u64_pipe = fn (file_handle: s32, value: u64) ?usize { + const file = FileDescriptor{ + .handle = file_handle, + }; + const value_ptr: [&]u8 = #cast(value.&); + const bytes = value_ptr[0..#size(u64)]; + return file.write(bytes); +} + +const read_u64_pipe = fn (file_handle: s32) ?u64{ + const file = FileDescriptor{ + .handle = file_handle, + }; + var value: u64 = 0; + const value_ptr: [&]u8 = #cast(value.&); + const bytes = value_ptr[0..#size(u64)]; + if (file.read(bytes)) |character_read_count| { + if (character_read_count == #size(u64)) { + return value; + } else { + return null; + } + } else { + return null; + } +} + +const termsig = fn(status: u32) u32 { + return status & 0x7f; +} + +const ifexited = fn(status: u32) bool { + return termsig(status) == 0; +} + +const exitstatus = fn(status: u32) u8 { + const result: u8 = #cast((status & 0xff00) >> 8); + return result; +} + +const stopsig = fn(status: u32) u32 { + return exitstatus(status); +} + +const ifstopped = fn(status: u32) bool { + const result: u16 = #cast(((status & 0xffff) * 0x10001) >> 8); + return result > 0x7f00; +} + +const ifsignaled = fn(status: u32) bool { + return (status & 0xffff) - 1 < 0xff; +} + +const waitpid = fn(pid: Process.Id, flags: u32) ?u32 { + switch (current) { + .linux => { + var status: u32 = undefined; + while (true) { + const raw_syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0); + const signed_syscall_result: ssize = #cast(raw_syscall_result); + if (raw_syscall_result != -4) { + if (linux.unwrapSyscall(syscall_result = raw_syscall_result)) |_| { + return status; + } else { + return null; + } + } + } + }, + else => #error("OS not supported"), + } + +} + +const IoChannelBehavior = enum{ + pipe, + close, + inherit, + ignore, +}; const linux = #import("os/linux.nat"); const macos = #import("os/macos.nat"); diff --git a/lib/std/os/linux.nat b/lib/std/os/linux.nat index 78246be..5e23615 100644 --- a/lib/std/os/linux.nat +++ b/lib/std/os/linux.nat @@ -435,6 +435,56 @@ const execve = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: return result; } +const event_file_descriptor = fn(count: u32, flags: u32) usize { + const result = #syscall(#cast(Syscall.eventfd2), #cast(count), #cast(flags)); + return result; +} + +const dup2 = fn(old: s32, new: s32) usize { + const result = #syscall(#cast(Syscall.dup2), #cast(old), #cast(new)); + return result; +} + +const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) usize { + const result = #syscall(#cast(Syscall.open), #cast(path), flags, permissions); + return result; +} + +const openat = fn(directory_file_descriptor: s32, path: [&:0]const u8, flags: u32, permissions: u32) usize { + const result = #syscall(#cast(Syscall.openat), #cast(directory_file_descriptor), #cast(path), flags, permissions); + return result; +} + +const read = fn(file_descriptor: s32, bytes_ptr: [&]u8, bytes_len: usize) usize { + const result = #syscall(#cast(Syscall.read), #cast(file_descriptor), #cast(bytes_ptr), bytes_len); + return result; +} + +const write = fn(file_descriptor: s32, bytes_ptr: [&]const u8, bytes_len: usize) usize { + const result = #syscall(#cast(Syscall.write), #cast(file_descriptor), #cast(bytes_ptr), bytes_len); + return result; +} + +const close = fn(file_descriptor: s32) usize { + const result = #syscall(#cast(Syscall.close), #cast(file_descriptor)); + return result; +} + +const pipe2 = fn (pipe_pointer: &[2]s32, flags: u32) usize { + const result = #syscall(#cast(Syscall.pipe2), #cast(pipe_pointer), flags); + return result; +} + +const waitpid = fn(pid: ProcessId, status: &u32, flags: u32, resource_usage: usize) usize { + const result = #syscall(#cast(Syscall.wait4), pid, #cast(status), flags, resource_usage); + return result; +} + +const poll = fn(file_descriptors: [&]PollFileDescriptor, file_descriptor_count: usize, timeout: s32) usize { + const result = #syscall(#cast(Syscall.poll), #cast(file_descriptors), file_descriptor_count, #cast(timeout)); + return result; +} + const unwrapSyscall = fn(syscall_result: usize) ?usize { const signed_syscall_result: ssize = #cast(syscall_result); if (signed_syscall_result >= 0) { @@ -443,3 +493,26 @@ const unwrapSyscall = fn(syscall_result: usize) ?usize { return null; } } + +const EventFileDescriptorFlags = enum(u32) { + semaphore = 1, + cloexec = 0o2000000, + nonblock = 0o4000, +}; + +const PollFileDescriptor = struct{ + file_descriptor: FileDescriptor, + events: Poll, + revents: Poll, + + const Poll = struct(u16) { + in: bool = false, + pri: bool = false, + out: bool = false, + err: bool = false, + hup: bool = false, + nval: bool = false, + rdnorm: bool = false, + rdband: bool = false, + }; +}; diff --git a/lib/std/start.nat b/lib/std/start.nat index 2d35fff..7e4f274 100644 --- a/lib/std/start.nat +++ b/lib/std/start.nat @@ -6,12 +6,24 @@ comptime { const _start = fn () noreturn export cc(.naked) { #asm({ xor ebp, ebp; + mov rdi, rsp; and rsp, 0xfffffffffffffff0; call {start}; }); } -const start = fn() noreturn export { +var argument_count: usize = 0; +var argument_values: [&]const [&:0]const u8 = undefined; +var environment_values: [&:null]const ?[&:null]const u8 = undefined; + +const start = fn(argc_argv_address: usize) noreturn export { + var argument_address_iterator = argc_argv_address; + const argument_count_ptr: &usize = #cast(argument_address_iterator); + argument_count = argument_count_ptr.@; + argument_address_iterator += #size(usize); + argument_values = #cast(argument_address_iterator); + argument_address_iterator += #size(usize) * (argument_count + 1); + environment_values = #cast(argument_address_iterator); const result = #import("main").main(); std.os.exit(exit_code = result); } diff --git a/test/div/main.nat b/test/div/main.nat index 65e1a2f..a319afc 100644 --- a/test/div/main.nat +++ b/test/div/main.nat @@ -1,4 +1,4 @@ -const main = fn () s32 { +const main = fn() s32 { const dividend: s32 = 30; const divisor: s32 = 6; const div: s32 = dividend / divisor; diff --git a/test/foreach/main.nat b/test/foreach/main.nat index 4f39fb7..cf91cd7 100644 --- a/test/foreach/main.nat +++ b/test/foreach/main.nat @@ -1,4 +1,4 @@ -const main = fn () s32 { +const main = fn() s32 { var counter: s32 = 0; const loop = 10; diff --git a/test/fork/main.nat b/test/fork/main.nat index 8e7f968..dfa54be 100644 --- a/test/fork/main.nat +++ b/test/fork/main.nat @@ -1,4 +1,5 @@ const std = #import("std"); + const main = fn() s32 { if (std.os.duplicate_process()) |pid| { if (pid == 0) { diff --git a/test/fork_exec/main.nat b/test/fork_exec/main.nat index faf1aac..df8bd18 100644 --- a/test/fork_exec/main.nat +++ b/test/fork_exec/main.nat @@ -1,11 +1,11 @@ const std = #import("std"); -const main = fn() s32{ + +const main = fn() s32 { if (std.os.duplicate_process()) |pid| { if (pid == 0) { std.print(bytes = "Hello from child\n"); const argv = [_:null] ?[&:0]const u8{"/usr/bin/ls"}; - const env = [_:null] ?[&:null]const u8 {}; - std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = env.&); + std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = std.start.environment_values); return 1; } else { std.print(bytes = "Hello from parent\n"); diff --git a/test/imul/main.nat b/test/imul/main.nat index f298a20..8033281 100644 --- a/test/imul/main.nat +++ b/test/imul/main.nat @@ -1,4 +1,4 @@ -const main = fn () s32 { +const main = fn() s32 { const a: s32 = 5; const b: s32 = 4; return a * b - a * b; diff --git a/test/loop_break/main.nat b/test/loop_break/main.nat new file mode 100644 index 0000000..ec8220a --- /dev/null +++ b/test/loop_break/main.nat @@ -0,0 +1,11 @@ +const main = fn() s32 { + var i: s32 = 0; + while (i < 10) { + i += 1; + if (i == 5) { + break; + } + } + + return i - 5; +} diff --git a/test/self_exe_path/main.nat b/test/self_exe_path/main.nat index 03d4703..3e1906f 100644 --- a/test/self_exe_path/main.nat +++ b/test/self_exe_path/main.nat @@ -1,8 +1,9 @@ const std = #import("std"); const print = std.print; -const main = fn () s32 { - if (std.os.current_executable_path(allocator = std.page_allocator.allocator.&)) |bytes| { +const main = fn() s32 { + var buffer: [std.os.max_path_byte_count + 1]u8 = undefined; + if (std.os.current_executable_path(buffer = buffer.&)) |bytes| { print(bytes); print(bytes = "\n"); return 0; diff --git a/test/simple_bool/main.nat b/test/simple_bool/main.nat index 088682b..65c1549 100644 --- a/test/simple_bool/main.nat +++ b/test/simple_bool/main.nat @@ -1,4 +1,4 @@ -const main = fn () s32 { +const main = fn() s32 { var false_boolean: bool = false; if (false_boolean) { return 1; diff --git a/test/virtual_memory/main.nat b/test/virtual_memory/main.nat index 387486a..6fcbf13 100644 --- a/test/virtual_memory/main.nat +++ b/test/virtual_memory/main.nat @@ -2,6 +2,7 @@ const std = #import("std"); const main = fn() s32 { const size = 0x1000; + if (std.page_allocator.allocate(size, alignment = 12)) |result| { result[0] = 0; std.print(bytes = "Allocation succeeded. Freeing...\n");