diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index 864fe8e..571bce8 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -3805,10 +3805,16 @@ pub const Instruction = union(enum) { const Id = enum { add, + wrapping_add, + saturated_add, + sub, + wrapping_sub, + saturated_sub, + mul, + wrapping_mul, + saturated_mul, div, mod, - mul, - sub, bit_and, bit_or, bit_xor, @@ -4380,8 +4386,14 @@ pub const IntrinsicId = enum { pub const ArithmeticLogicIntegerInstruction = enum { add, + wrapping_add, + saturated_add, sub, + wrapping_sub, + saturated_sub, mul, + wrapping_mul, + saturated_mul, div, mod, bit_and, @@ -6728,7 +6740,7 @@ pub const Builder = struct { fn resolveAssignment(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index) !V { const node = unit.getNode(node_index); switch (node.id) { - .assign, .add_assign, .sub_assign, .div_assign => { + .assign, .add_assign, .sub_assign, .div_assign, .or_assign => { if (unit.getNode(node.left).id == .discard) { _ = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.right, .right); return .{ @@ -6778,6 +6790,7 @@ pub const Builder = struct { .add_assign => .add, .sub_assign => .sub, .div_assign => .div, + .or_assign => .bit_or, else => |t| @panic(@tagName(t)), }, }, @@ -9992,13 +10005,22 @@ pub const Builder = struct { }; } }, - .add, .sub, .mul, .div, .mod, .bit_and, .bit_or, .bit_xor, .shift_left, .shift_right, .bool_and, .bool_or => block: { + .add, .wrapping_add, .saturated_add, + .sub, .wrapping_sub, .saturated_sub, + .mul, .wrapping_mul, .saturated_mul, + .div, .mod, .bit_and, .bit_or, .bit_xor, .shift_left, .shift_right, .bool_and, .bool_or => block: { const left_node_index = node.left; const right_node_index = node.right; const binary_operation_id: ArithmeticLogicIntegerInstruction = switch (node.id) { .add => .add, + .wrapping_add => .wrapping_add, + .saturated_add => .saturated_add, .sub => .sub, + .wrapping_sub => .wrapping_sub, + .saturated_sub => .saturated_sub, .mul => .mul, + .wrapping_mul => .wrapping_mul, + .saturated_mul => .saturated_mul, .div => .div, .mod => .mod, .bit_and => .bit_and, @@ -10026,11 +10048,17 @@ pub const Builder = struct { }, .type => switch (binary_operation_id) { .add, + .wrapping_add, + .saturated_add, .sub, + .wrapping_sub, + .saturated_sub, .bit_and, .bit_xor, .bit_or, .mul, + .wrapping_mul, + .saturated_mul, .div, .mod, .shift_left, @@ -10070,6 +10098,7 @@ pub const Builder = struct { .bit_xor => left ^ right, .shift_left => left << @as(u6, @intCast(right)), .shift_right => left >> @as(u6, @intCast(right)), + else => unreachable, }; break :block switch (type_expect) { @@ -10141,8 +10170,14 @@ pub const Builder = struct { .bit_xor, .shift_right, .add, + .wrapping_add, + .saturated_add, .sub, + .wrapping_sub, + .saturated_sub, .mul, + .wrapping_mul, + .saturated_mul, .div, .mod, => left_value.type, @@ -10157,10 +10192,16 @@ pub const Builder = struct { .materialized_int => b: { const id: Instruction.IntegerBinaryOperation.Id = switch (binary_operation_id) { .add => .add, + .wrapping_add => .wrapping_add, + .saturated_add => .saturated_add, + .sub => .sub, + .wrapping_sub => .wrapping_sub, + .saturated_sub => .saturated_sub, + .mul => .mul, + .wrapping_mul => .wrapping_mul, + .saturated_mul => .saturated_mul, .div => .div, .mod => .mod, - .mul => .mul, - .sub => .sub, .bit_and => .bit_and, .bit_or => .bit_or, .bit_xor => .bit_xor, @@ -13776,7 +13817,7 @@ pub const Builder = struct { try builder.insertDebugCheckPoint(unit, context, statement_node.token); switch (statement_node.id) { - .assign, .add_assign, .sub_assign, .div_assign => { + .assign, .add_assign, .sub_assign, .div_assign, .or_assign => { _ = try builder.resolveAssignment(unit, context, statement_node_index); }, .if_else => { @@ -17349,8 +17390,14 @@ pub const Token = struct { // Binary operator_assign, operator_add, + operator_saturated_add, + operator_wrapping_add, operator_minus, + operator_saturated_sub, + operator_wrapping_sub, operator_asterisk, + operator_saturated_mul, + operator_wrapping_mul, operator_div, operator_mod, operator_bar, @@ -17359,8 +17406,14 @@ pub const Token = struct { operator_shift_left, operator_shift_right, operator_add_assign, + operator_wrapping_add_assign, + operator_saturated_add_assign, operator_sub_assign, + operator_wrapping_sub_assign, + operator_saturated_sub_assign, operator_mul_assign, + operator_wrapping_mul_assign, + operator_saturated_mul_assign, operator_div_assign, operator_mod_assign, operator_or_assign, diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index d2abfe0..a98111d 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -2762,14 +2762,20 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const left = try llvm.emitRightValue(unit, context, binary_operation.left); const right = try llvm.emitRightValue(unit, context, binary_operation.right); assert(left.getType() == right.getType()); - const no_signed_wrapping = binary_operation.signedness == .signed; - const no_unsigned_wrapping = binary_operation.signedness == .unsigned; + const no_signed_wrapping = binary_operation.signedness == .signed or switch (binary_operation.id) { + .wrapping_add, .wrapping_sub, .wrapping_mul => false, + else => true, + }; + const no_unsigned_wrapping = binary_operation.signedness == .unsigned or switch (binary_operation.id) { + .wrapping_add, .wrapping_sub, .wrapping_mul => false, + else => true, + }; const name = @tagName(binary_operation.id); const is_exact = false; const instruction = switch (binary_operation.id) { - .add => llvm.builder.createAdd(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, - .mul => llvm.builder.createMultiply(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.multiply, - .sub => llvm.builder.createSub(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, + .add, .wrapping_add => llvm.builder.createAdd(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, + .sub, .wrapping_sub => llvm.builder.createSub(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, + .mul, .wrapping_mul => llvm.builder.createMultiply(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.multiply, .div => switch (binary_operation.signedness) { .unsigned => llvm.builder.createUDiv(left, right, name.ptr, name.len, is_exact) orelse unreachable, .signed => llvm.builder.createSDiv(left, right, name.ptr, name.len, is_exact) orelse unreachable, @@ -2786,7 +2792,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo .unsigned => llvm.builder.createLogicalShiftRight(left, right, name.ptr, name.len, is_exact) orelse unreachable, .signed => llvm.builder.createArithmeticShiftRight(left, right, name.ptr, name.len, is_exact) orelse unreachable, }, - //else => |t| @panic(@tagName(t)), + else => unreachable, }; try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, instruction); }, diff --git a/bootstrap/frontend/lexer.zig b/bootstrap/frontend/lexer.zig index e7f6613..b125f62 100644 --- a/bootstrap/frontend/lexer.zig +++ b/bootstrap/frontend/lexer.zig @@ -313,6 +313,26 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B index += 1; break :b .operator_add_assign; }, + '|' => b: { + index += 1; + break :b switch (text[index]) { + '=' => assign: { + index += 1; + break :assign .operator_saturated_add_assign; + }, + else => .operator_saturated_add, + }; + }, + '%' => b: { + index += 1; + break :b switch (text[index]) { + '=' => assign: { + index += 1; + break :assign .operator_wrapping_add_assign; + }, + else => .operator_wrapping_add, + }; + }, else => .operator_add, }; @@ -325,6 +345,26 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B index += 1; break :b .operator_sub_assign; }, + '|' => b: { + index += 1; + break :b switch (text[index]) { + '=' => assign: { + index += 1; + break :assign .operator_saturated_add_assign; + }, + else => .operator_saturated_sub, + }; + }, + '%' => b: { + index += 1; + break :b switch (text[index]) { + '=' => assign: { + index += 1; + break :assign .operator_wrapping_sub_assign; + }, + else => .operator_wrapping_sub, + }; + }, else => .operator_minus, }; @@ -337,6 +377,26 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B index += 1; break :b .operator_mul_assign; }, + '|' => b: { + index += 1; + break :b switch (text[index]) { + '=' => assign: { + index += 1; + break :assign .operator_saturated_mul_assign; + }, + else => .operator_saturated_mul, + }; + }, + '%' => b: { + index += 1; + break :b switch (text[index]) { + '=' => assign: { + index += 1; + break :assign .operator_wrapping_mul_assign; + }, + else => .operator_wrapping_mul, + }; + }, else => .operator_asterisk, }; diff --git a/bootstrap/frontend/parser.zig b/bootstrap/frontend/parser.zig index 16bff72..6e22306 100644 --- a/bootstrap/frontend/parser.zig +++ b/bootstrap/frontend/parser.zig @@ -206,6 +206,13 @@ pub const Node = struct { slice_metadata, orelse_expression, type, + or_assign, + wrapping_add, + saturated_add, + wrapping_sub, + saturated_sub, + wrapping_mul, + saturated_mul, }; }; @@ -871,6 +878,7 @@ const Analyzer = struct { .operator_mul_assign => .mul_assign, .operator_div_assign => .div_assign, .operator_mod_assign => .mod_assign, + .operator_or_assign => .or_assign, else => |t| @panic(@tagName(t)), }; analyzer.consumeToken(); @@ -1052,8 +1060,14 @@ const Analyzer = struct { compare_less_equal, compare_greater_equal, add, + wrapping_add, + saturated_add, sub, + wrapping_sub, + saturated_sub, mul, + wrapping_mul, + saturated_mul, div, mod, bit_and, @@ -1075,8 +1089,14 @@ const Analyzer = struct { .compare_less_equal = 30, .compare_greater_equal = 30, .add = 60, + .wrapping_add = 60, + .saturated_add = 60, .sub = 60, + .wrapping_sub = 60, + .saturated_sub = 60, .mul = 70, + .wrapping_mul = 70, + .saturated_mul = 70, .div = 70, .mod = 70, .bit_and = 40, @@ -1098,13 +1118,19 @@ const Analyzer = struct { .compare_less_equal = .none, .compare_greater_equal = .none, .add = .left, + .wrapping_add = .left, + .saturated_add = .left, .sub = .left, + .wrapping_sub = .left, + .saturated_sub = .left, .bit_and = .left, .bit_xor = .left, .bit_or = .left, .bool_and = .left, .bool_or = .left, .mul = .left, + .wrapping_mul = .left, + .saturated_mul = .left, .div = .left, .mod = .left, .shift_left = .left, @@ -1121,13 +1147,19 @@ const Analyzer = struct { .compare_greater_equal = .compare_greater_equal, .compare_less_equal = .compare_less_equal, .add = .add, + .wrapping_add = .wrapping_add, + .saturated_add = .saturated_add, .sub = .sub, + .wrapping_sub = .wrapping_sub, + .saturated_sub = .saturated_sub, .bit_and = .bit_and, .bit_xor = .bit_xor, .bit_or = .bit_or, .bool_and = .bool_and, .bool_or = .bool_or, .mul = .mul, + .wrapping_mul = .wrapping_mul, + .saturated_mul = .saturated_mul, .div = .div, .mod = .mod, .shift_left = .shift_left, @@ -1162,6 +1194,7 @@ const Analyzer = struct { .operator_mul_assign, .operator_div_assign, .operator_mod_assign, + .operator_or_assign, .operator_dot, .operator_double_dot, .operator_triple_dot, @@ -1183,8 +1216,14 @@ const Analyzer = struct { .operator_compare_less_equal => .compare_less_equal, .operator_compare_greater_equal => .compare_greater_equal, .operator_add => .add, + .operator_wrapping_add => .wrapping_add, + .operator_saturated_add => .saturated_add, .operator_minus => .sub, + .operator_wrapping_sub => .wrapping_sub, + .operator_saturated_sub => .saturated_sub, .operator_asterisk => .mul, + .operator_wrapping_mul => .wrapping_mul, + .operator_saturated_mul => .saturated_mul, .operator_div => .div, .operator_mod => .mod, .operator_ampersand => .bit_and, diff --git a/src/main.nat b/src/main.nat index 9cf3f0c..57695d8 100644 --- a/src/main.nat +++ b/src/main.nat @@ -6,6 +6,39 @@ const print = std.print; const print_usize = std.print_usize; const exit = std.os.exit; +const lex = fn (arena: &Arena, bytes: []const u8) *!void { + if (bytes.length >= 0xffffffff) { + unreachable; + } + + const length: u32 = #cast(bytes.length); + + const max_initial_keyword_len: u32 = 8; + + //var index: u32 = 0; + //while (index + 64 < length) { + // var i = index; + // const top = index + max_initial_keyword_len + 1; + // var space: u32 = 0; + // while (i < top) { + // const is_space = bytes[i] == ' '; + // space |= #cast( + // i += 1; + // } + + // if (space == 0) { + // unreachable; + // } + //} +} + +const FileStartToken = enum{ + "comptime", + "test", + "const", + "var", +}; + const ArgumentProcessingError = error{ no_arguments, }; @@ -58,31 +91,6 @@ const FixedKeyword = enum{ "continue", }; -const FileStartToken = enum{ - "comptime", - "test", - "const", - "var", -}; - -const lex = fn (arena: &Arena, bytes: []const u8) *!void { - if (bytes.length >= 0xffffffff) { - unreachable; - } - - const length: u32 = #cast(bytes.length); - var i: u32 = 0; - - //var index: u32 = 0; - //while (index + 64 < length) { - // var i = index; - // const top = index + 64; - // while (i < top) { - // i += 1; - // } - //} -} - const get_argument = fn (real_argument: []const u8, wanted_argument: []const u8, command_arguments: []const [&:0]const u8, i_ptr: &usize) ?[]const u8 { const i = i_ptr.@; diff --git a/test/standalone/wrapping_arithmetic/main.nat b/test/standalone/wrapping_arithmetic/main.nat new file mode 100644 index 0000000..5b049ca --- /dev/null +++ b/test/standalone/wrapping_arithmetic/main.nat @@ -0,0 +1,12 @@ +const std = #import("std"); +const expect = std.testing.expect; +const main = fn () *!void { + var a: u8 = 255; + var b: u8 = 1; + const result = a +% b; + try expect(result == 0); + var c: u16 = 0; + var d: u16 = 1; + const sub_result = c -% d; + try expect(sub_result == 65535); +}