diff --git a/bootstrap/compiler.zig b/bootstrap/compiler.zig index e34978a..09f2c02 100644 --- a/bootstrap/compiler.zig +++ b/bootstrap/compiler.zig @@ -282,55 +282,93 @@ const Parser = struct{ } } - fn parse_typed_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, ty: *Type, side: Side) *Value { - _ = side; // autofix - _ = &analyzer; + fn parse_constant_integer(parser: *Parser, thread: *Thread, file: *File, ty: *Type) *ConstantInt { const src = file.source_code; - assert(ty.sema.id != .unresolved); - const starting_ch = src[parser.i]; + const starting_index = parser.i; + const starting_ch = src[starting_index]; + if (starting_ch == '0') { + const follow_up_character = src[parser.i + 1]; + const is_hex_start = follow_up_character == 'x'; + const is_octal_start = follow_up_character == 'o'; + const is_bin_start = follow_up_character == 'b'; + const is_prefixed_start = is_hex_start or is_octal_start or is_bin_start; + const follow_up_alpha = is_alphabetic(follow_up_character); + const follow_up_digit = is_decimal_digit(follow_up_character); + const is_valid_after_zero = is_space(follow_up_character) or (!follow_up_digit and !follow_up_alpha); + + if (is_prefixed_start) { + exit(1); + } else if (is_valid_after_zero) { + parser.i += 1; + const constant_int = thread.constant_ints.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .constant_int, + }, + }, + .n = 0, + .type = ty, + }); + return constant_int; + } else { + exit(1); + } + } + + while (is_decimal_digit(src[parser.i])) { + parser.i += 1; + } + + const character_count = parser.i - starting_index; + const slice = src[starting_index..][0..character_count]; + var i = character_count; + var integer: u64 = 0; + var factor: u64 = 1; + + while (i > 0) { + i -= 1; + const ch = slice[i]; + const int = ch - '0'; + const extra = int * factor; + integer += extra; + factor *= 10; + } + + const constant_int = thread.constant_ints.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .constant_int, + }, + }, + .n = integer, + .type = ty, + }); + + return constant_int; + } + + fn parse_single_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, maybe_type: ?*Type, side: Side) *Value { + _ = side; // autofix + const src = file.source_code; + const starting_index = parser.i; + const starting_ch = src[starting_index]; const is_digit_start = is_decimal_digit(starting_ch); const is_alpha_start = is_alphabetic(starting_ch); - if (is_digit_start) { + const ty = maybe_type orelse exit(1); switch (ty.sema.id) { .integer => { - if (starting_ch == '0') { - const follow_up_character = src[parser.i + 1]; - const is_hex_start = follow_up_character == 'x'; - const is_octal_start = follow_up_character == 'o'; - const is_bin_start = follow_up_character == 'b'; - const is_prefixed_start = is_hex_start or is_octal_start or is_bin_start; - const follow_up_alpha = is_alphabetic(follow_up_character); - const follow_up_digit = is_decimal_digit(follow_up_character); - const is_valid_after_zero = is_space(follow_up_character) or (!follow_up_digit and !follow_up_alpha); - - if (is_prefixed_start) { - exit(1); - } else if (is_valid_after_zero) { - parser.i += 1; - const constant_int = thread.constant_ints.append(.{ - .value = .{ - .sema = .{ - .thread = thread.get_index(), - .resolved = true, - .id = .constant_int, - }, - }, - .n = 0, - .type = ty, - }); - return &constant_int.value; - } else { - exit(1); - } - } - exit(0); + const constant_int = parser.parse_constant_integer(thread, file, ty); + return &constant_int.value; }, else => unreachable, } } else if (is_alpha_start) { var resolved = true; - _ = &resolved; // autofix const identifier = parser.parse_identifier(thread, src); if (analyzer.current_scope.get_declaration(identifier)) |lookup_result| { @@ -375,7 +413,7 @@ const Parser = struct{ .resolved = false, .id = .instruction, }, - }, + }, .id = .call, }, .callable = &lazy_expression.value, @@ -393,36 +431,37 @@ const Parser = struct{ }, else => |t| @panic(@tagName(t)), } - exit(1); }, - ';' => { + ' ', ';' => { switch (lookup_result.declaration.*.id) { .local => { const local_declaration = lookup_result.declaration.*.get_payload(.local); const local_symbol = local_declaration.to_symbol(); - switch (typecheck(ty, local_symbol.type)) { - .success => { - const load = thread.loads.append(.{ - .instruction = .{ - .value = .{ - .sema = .{ - .thread = thread.get_index(), - .resolved = true, - .id = .instruction, - }, - }, - .id = .load, - }, - .value = &local_symbol.instruction.value, - .type = local_symbol.type, - .alignment = local_symbol.type.alignment, - .is_volatile = false, - }); - _ = analyzer.current_basic_block.instructions.append(&load.instruction); - return &load.instruction.value; - }, + if (maybe_type) |ty| { + switch (typecheck(ty, local_symbol.type)) { + .success => {}, + } } + + const load = thread.loads.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .load, + }, + .value = &local_symbol.instruction.value, + .type = local_symbol.type, + .alignment = local_symbol.type.alignment, + .is_volatile = false, + }); + _ = analyzer.current_basic_block.instructions.append(&load.instruction); + return &load.instruction.value; }, else => |t| @panic(@tagName(t)), } @@ -434,6 +473,88 @@ const Parser = struct{ exit(1); } } + + const CurrentOperation = enum{ + none, + add, + add_assign, + sub, + sub_assign, + }; + + fn parse_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, ty: ?*Type, side: Side) *Value { + const src = file.source_code; + + var current_operation = CurrentOperation.none; + var previous_value: *Value = undefined; + while (true) { + const current_value = parser.parse_single_expression(analyzer, thread, file, ty, side); + parser.skip_space(src); + + switch (current_operation) { + .none => { + previous_value = current_value; + }, + .add, .sub => { + const add = thread.integer_binary_operations.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .integer_binary_operation, + }, + .left = previous_value, + .right = current_value, + .id = switch (current_operation) { + .none, .add_assign, .sub_assign => unreachable, + inline else => |co| @field(IntegerBinaryOperation.Id, @tagName(co)), + }, + .type = if (ty) |t| t else current_value.get_type(), + }); + _ = analyzer.current_basic_block.instructions.append(&add.instruction); + previous_value = &add.instruction.value; + }, + .add_assign, .sub_assign => unreachable, + } + + switch (src[parser.i]) { + ';' => return previous_value, + '+' => { + current_operation = .add; + parser.i += 1; + + switch (src[parser.i]) { + '=' => { + current_operation = .add_assign; + parser.i += 1; + }, + else => {}, + } + + parser.skip_space(src); + }, + '-' => { + current_operation = .sub; + parser.i += 1; + + switch (src[parser.i]) { + '=' => { + current_operation = .sub_assign; + parser.i += 1; + }, + else => {}, + } + + parser.skip_space(src); + }, + else => @panic((src.ptr + parser.i)[0..1]), + } + } + } }; const LazyExpression = struct { @@ -494,20 +615,6 @@ const LazyExpression = struct { } }; -// fn Descriptor(comptime Id: type, comptime Integer: type) type { -// return packed struct(Integer) { -// index: @Type(.{ -// .Int = .{ -// .signedness = .unsigned, -// .bits = @typeInfo(Integer).Int.bits - @typeInfo(@typeInfo(Id).Enum.tag_type).Int.bits, -// }, -// }), -// id: Id, -// -// pub const Index = PinnedArray(@This()).Index; -// }; -// } - const Value = struct { llvm: ?*LLVM.Value = null, sema: packed struct(u32) { @@ -535,6 +642,26 @@ const Value = struct { assert(value.sema.id == id); return @fieldParentPtr("value", value); } + + fn get_type(value: *Value) *Type { + return switch (value.sema.id) { + .instruction => blk: { + const instruction = value.get_payload(.instruction); + break :blk switch (instruction.id) { + .integer_binary_operation => block: { + const bin_op = instruction.get_payload(.integer_binary_operation); + break :block bin_op.type; + }, + .load => block: { + const load = instruction.get_payload(.load); + break :block load.type; + }, + else => |t| @panic(@tagName(t)), + }; + }, + else => |t| @panic(@tagName(t)), + }; + } }; const Type = struct { @@ -747,11 +874,18 @@ const Function = struct{ }; }; +const ConstantInt = struct{ + value: Value, + n: u64, + type: *Type, +}; + const Instruction = struct{ value: Value, id: Id, const Id = enum{ + integer_binary_operation, call, load, local_symbol, @@ -762,6 +896,7 @@ const Instruction = struct{ const id_to_instruction_map = std.EnumArray(Id, type).init(.{ .call = Call, + .integer_binary_operation = IntegerBinaryOperation, .local_symbol = LocalSymbol, .load = Load, .ret = Return, @@ -775,10 +910,17 @@ const Instruction = struct{ } }; -const ConstantInt = struct{ - value: Value, - n: u64, +const IntegerBinaryOperation = struct { + instruction: Instruction, + left: *Value, + right: *Value, type: *Type, + id: Id, + + const Id = enum{ + add, + sub, + }; }; const Call = struct{ @@ -839,6 +981,7 @@ const Thread = struct{ debug_info_file_map: PinnedHashMap(u32, LLVMFile) = .{}, // pending_values_per_file: PinnedArray(PinnedArray(*Value)) = .{}, calls: PinnedArray(Call) = .{}, + integer_binary_operations: PinnedArray(IntegerBinaryOperation) = .{}, loads: PinnedArray(Load) = .{}, stores: PinnedArray(Store) = .{}, returns: PinnedArray(Return) = .{}, @@ -1991,15 +2134,12 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void { for (nat_entry_basic_block.instructions.slice()) |instruction| { const value: *LLVM.Value = switch (instruction.id) { - .call => block: { - const call = instruction.get_payload(.call); - const callee = llvm_get_value(thread, call.callable); - const callee_function = callee.toFunction() orelse unreachable; - const function_type = callee_function.getType(); - - const arguments: []const *LLVM.Value = &.{}; - const call_i = thread.llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, "", "".len, null); - break :block call_i.toValue(); + .store => block: { + const store = instruction.get_payload(.store); + const destination = llvm_get_value(thread, store.destination); + const source = llvm_get_value(thread, store.source); + const store_instruction = builder.createStore(source, destination, store.is_volatile, store.alignment); + break :block store_instruction.toValue(); }, .load => block: { const load = instruction.get_payload(.load); @@ -2009,19 +2149,34 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void { const load_instruction = builder.createLoad(load_type, load_value, load.is_volatile, "", "".len, load.alignment); break :block load_instruction.toValue(); }, - .store => block: { - const store = instruction.get_payload(.store); - const destination = llvm_get_value(thread, store.destination); - const source = llvm_get_value(thread, store.source); - const store_instruction = builder.createStore(source, destination, store.is_volatile, store.alignment); - break :block store_instruction.toValue(); - }, .ret => block: { const return_instruction = instruction.get_payload(.ret); const return_value = llvm_get_value(thread, return_instruction.value); const ret = thread.llvm.builder.createRet(return_value); break :block ret.toValue(); }, + .integer_binary_operation => block: { + const integer_binary_operation = instruction.get_payload(.integer_binary_operation); + const left = llvm_get_value(thread, integer_binary_operation.left); + const right = llvm_get_value(thread, integer_binary_operation.right); + const integer_type = integer_binary_operation.type.get_payload(.integer); + const no_unsigned_wrapping = integer_type.signedness == .unsigned; + const no_signed_wrapping = integer_type.signedness == .signed; + break :block switch (integer_binary_operation.id) { + .add => builder.createAdd(left, right, "", "".len, no_unsigned_wrapping, no_signed_wrapping), + .sub => builder.createSub(left, right, "", "".len, no_unsigned_wrapping, no_signed_wrapping), + }; + }, + .call => block: { + const call = instruction.get_payload(.call); + const callee = llvm_get_value(thread, call.callable); + const callee_function = callee.toFunction() orelse unreachable; + const function_type = callee_function.getType(); + + const arguments: []const *LLVM.Value = &.{}; + const call_i = thread.llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, "", "".len, null); + break :block call_i.toValue(); + }, else => |t| @panic(@tagName(t)), }; @@ -2056,12 +2211,14 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void { } const verify_module = true; + const print_module_at_failure = true; + const print_module = false; + if (verify_module) { var verification_message: []const u8 = undefined; const verification_success = thread.llvm.module.verify(&verification_message.ptr, &verification_message.len); if (!verification_success) { - const print_module = true; - if (print_module) { + if (print_module_at_failure) { var module_content: []const u8 = undefined; thread.llvm.module.toString(&module_content.ptr, &module_content.len); write(module_content); @@ -2072,6 +2229,13 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void { } } + if (print_module) { + var module_content: []const u8 = undefined; + thread.llvm.module.toString(&module_content.ptr, &module_content.len); + write(module_content); + write("\n"); + } + thread.add_control_work(.{ .id = .llvm_notify_ir_done, }); @@ -2316,7 +2480,7 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser parser.skip_space(src); if (function.declaration.return_type.sema.id != .unresolved) { - const return_value = parser.parse_typed_expression(analyzer, thread, file, function.declaration.return_type, .right); + const return_value = parser.parse_expression(analyzer, thread, file, function.declaration.return_type, .right); parser.expect_character(src, ';'); const return_expression = thread.returns.append(.{ @@ -2362,20 +2526,51 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser exit_with_error("TODO: local attributes"); } - parser.expect_character(src, ':'); - parser.skip_space(src); - const local_type = parser.parse_type_expression(thread, src); + const LocalResult = struct { + initial_value: *Value, + type: *Type, + }; + const result: LocalResult = switch (src[parser.i]) { + ':' => block: { + parser.i += 1; + + parser.skip_space(src); + + const local_type = parser.parse_type_expression(thread, src); + + parser.skip_space(src); + parser.expect_character(src, '='); + + parser.skip_space(src); + + const local_initial_value = parser.parse_expression(analyzer, thread, file, local_type, .right); + + break :block .{ + .initial_value = local_initial_value, + .type = local_type, + }; + }, + '=' => block: { + parser.i += 1; + + parser.skip_space(src); + + const local_initial_value = parser.parse_expression(analyzer, thread, file, null, .right); + + const local_type = local_initial_value.get_type(); + + break :block .{ + .initial_value = local_initial_value, + .type = local_type, + }; + }, + else => exit(1), + }; parser.skip_space(src); - parser.expect_character(src, '='); - - parser.skip_space(src); - - const local_initial_value = parser.parse_typed_expression(analyzer, thread, file, local_type, .right); - parser.expect_character(src, ';'); const local_symbol = thread.local_symbols.append(.{ @@ -2384,19 +2579,19 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser .id = .local, .name = local_name, }, - }, - .type = local_type, + }, + .type = result.type, .instruction = .{ .value = .{ .sema = .{ .thread = thread.get_index(), - .resolved = local_type.sema.resolved and local_initial_value.sema.resolved, + .resolved = result.type.sema.resolved and result.initial_value.sema.resolved, .id = .instruction, }, - }, + }, .id = .local_symbol, }, - .alignment = local_type.alignment, + .alignment = result.type.alignment, }); _ = analyzer.current_function.stack_slots.append(local_symbol); @@ -2409,11 +2604,11 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser .resolved = true, .id = .instruction, }, - }, + }, .id = .store, }, .destination = &local_symbol.instruction.value, - .source = local_initial_value, + .source = result.initial_value, .alignment = local_symbol.alignment, .is_volatile = false, }); diff --git a/retest/standalone/add_sub/main.nat b/retest/standalone/add_sub/main.nat new file mode 100644 index 0000000..24a6f1f --- /dev/null +++ b/retest/standalone/add_sub/main.nat @@ -0,0 +1,8 @@ +fn [cc(.c)] main [export]() s32 { + >a: s32 = 1; + >b: s32 = 2; + >c = a + b; + >d: s32 = 3; + >e = d - c; + return e; +}