7 files changed, 57 insertions(+), 25 deletions(-)
CHANGELOG.md+1| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | - feat: implement string identifiers | 3 | - feat: implement string identifiers |
| 4 | - feat: implement basic struct types | 4 | - feat: implement basic struct types |
| 5 | - feat: include a `README.html` alongside compiler with documentation and code samples. | 5 | - feat: include a `README.html` alongside compiler with documentation and code samples. |
| 6 | - feat(transform): include a warning for leading zero in numbers | ||
| 6 | - fix(sema): miscompilation using left side of all binary expression for both sides | 7 | - fix(sema): miscompilation using left side of all binary expression for both sides |
| 7 | - fix(sema): handle recursive function calls | 8 | - fix(sema): handle recursive function calls |
| 8 | - fix(sema): quit block analysis when hitting `noreturn` | 9 | - fix(sema): quit block analysis when hitting `noreturn` |
src/core/Ast.zig+1-1| ... | @@ -763,7 +763,7 @@ pub fn extraData(tree: Ast, index: usize, comptime T: type) T { | ... | @@ -763,7 +763,7 @@ pub fn extraData(tree: Ast, index: usize, comptime T: type) T { |
| 763 | OptionalExtraIndex, | 763 | OptionalExtraIndex, |
| 764 | => 1, | 764 | => 1, |
| 765 | SubList => 2, | 765 | SubList => 2, |
| 766 | else => comptime unreachable, | 766 | else => @compileError(unreachable), |
| 767 | }; | 767 | }; |
| 768 | } | 768 | } |
| 769 | return result; | 769 | return result; |
src/core/Parse.zig+1-1| ... | @@ -867,7 +867,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -867,7 +867,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 867 | .kw_comptime => .@"comptime", | 867 | .kw_comptime => .@"comptime", |
| 868 | .kw_pure => .pure, | 868 | .kw_pure => .pure, |
| 869 | .kw_assert => .assert, | 869 | .kw_assert => .assert, |
| 870 | else => comptime unreachable, | 870 | else => @compileError(unreachable), |
| 871 | }, | 871 | }, |
| 872 | .main_token = main_token, | 872 | .main_token = main_token, |
| 873 | .data = .{ .un = .{ | 873 | .data = .{ .un = .{ |
src/core/Report.zig+44-15| ... | @@ -424,6 +424,25 @@ pub const Warn = union(enum) { | ... | @@ -424,6 +424,25 @@ pub const Warn = union(enum) { |
| 424 | try ctx.msgAlone("if this was intentional, discard it with \"_ = {};\"", .{formatIdCode(id)}); | 424 | try ctx.msgAlone("if this was intentional, discard it with \"_ = {};\"", .{formatIdCode(id)}); |
| 425 | } | 425 | } |
| 426 | }, | 426 | }, |
| 427 | zero_prefixed_literal: struct { | ||
| 428 | token: TokenIndex, | ||
| 429 | |||
| 430 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 431 | try ctx.msg(.{ | ||
| 432 | .token_substring = .{ | ||
| 433 | .token = data.token, | ||
| 434 | .start = 0, | ||
| 435 | .len = brk: { | ||
| 436 | const slice = ctx.ast.tokenSlice(data.token); | ||
| 437 | var i: u32 = 0; | ||
| 438 | while (slice[i] == '0') i += 1; | ||
| 439 | if (slice[i] == '.') i -= 1; | ||
| 440 | break :brk i; | ||
| 441 | }, | ||
| 442 | }, | ||
| 443 | }, "avoid redundant zeros in numbers", .{}); | ||
| 444 | } | ||
| 445 | }, | ||
| 427 | not_mutated_var: struct { | 446 | not_mutated_var: struct { |
| 428 | node: NodeIndex, | 447 | node: NodeIndex, |
| 429 | 448 | ||
| ... | @@ -814,19 +833,21 @@ const Component = struct { | ... | @@ -814,19 +833,21 @@ const Component = struct { |
| 814 | }; | 833 | }; |
| 815 | 834 | ||
| 816 | pub const Highlight = union(enum) { | 835 | pub const Highlight = union(enum) { |
| 817 | // No source view | 836 | /// No source view |
| 818 | alone, | 837 | alone, |
| 819 | // Highlight one token | 838 | /// Highlight one token |
| 820 | token: TokenIndex, | 839 | token: TokenIndex, |
| 821 | // Highlight one token without referring to the Ast | 840 | /// Highlight one token without referring to the Ast |
| 822 | token_stored: Tokenizer.Token, | 841 | token_stored: Tokenizer.Token, |
| 823 | // Highlight two tokens | 842 | /// Highlight two tokens |
| 824 | two_tokens: TokenIndex, | 843 | two_tokens: TokenIndex, |
| 825 | // Highlight a Node | 844 | /// Highlight a substring of a token |
| 845 | token_substring: struct { token: TokenIndex, start: ByteOffset, len: u32 }, | ||
| 846 | /// Highlight a Node | ||
| 826 | node: NodeIndex, | 847 | node: NodeIndex, |
| 827 | // Highlight a codepoint | 848 | /// Highlight a codepoint |
| 828 | codepoint: ByteOffset, | 849 | codepoint: ByteOffset, |
| 829 | // Highlight from the start of a codepoint to '\n' | 850 | /// Highlight from the start of a codepoint to '\n' |
| 830 | codepoint_eol: ByteOffset, | 851 | codepoint_eol: ByteOffset, |
| 831 | 852 | ||
| 832 | pub fn loc(highlight: Highlight, ast: *const Ast) ?SliceLoc { | 853 | pub fn loc(highlight: Highlight, ast: *const Ast) ?SliceLoc { |
| ... | @@ -834,22 +855,30 @@ pub const Highlight = union(enum) { | ... | @@ -834,22 +855,30 @@ pub const Highlight = union(enum) { |
| 834 | .alone => null, | 855 | .alone => null, |
| 835 | inline .node, | 856 | inline .node, |
| 836 | .token, | 857 | .token, |
| 858 | .token_substring, | ||
| 837 | .two_tokens, | 859 | .two_tokens, |
| 838 | => |payload, tag| loc: { | 860 | => |payload, tag| loc: { |
| 839 | const token_index: TokenIndex = switch (tag) { | 861 | const token_index: TokenIndex = switch (tag) { |
| 840 | .node => ast.nodeToken(payload), | 862 | .node => ast.nodeToken(payload), |
| 841 | .token, .two_tokens => payload, | 863 | .token, .two_tokens => payload, |
| 842 | else => comptime unreachable, | 864 | .token_substring => payload.token, |
| 865 | else => @compileError(unreachable), | ||
| 843 | }; | 866 | }; |
| 844 | 867 | ||
| 845 | const location = ast.tokenLocation(0, token_index); | ||
| 846 | const token = ast.tokens.get(token_index); | 868 | const token = ast.tokens.get(token_index); |
| 847 | 869 | ||
| 848 | break :loc .{ | 870 | var location = ast.tokenLocation(0, token_index); |
| 849 | .loc = location, | 871 | var slice = token.slice(ast.utf8_source); |
| 850 | .slice = token.slice(ast.utf8_source), | 872 | var byte = token.offset; |
| 851 | .byte = token.offset, | 873 | |
| 852 | }; | 874 | if (tag == .token_substring) { |
| 875 | location.column = location.column.add(payload.start); | ||
| 876 | slice.ptr += payload.start; | ||
| 877 | slice.len = payload.len; | ||
| 878 | byte += payload.start; | ||
| 879 | } | ||
| 880 | |||
| 881 | break :loc .{ .loc = location, .slice = slice, .byte = byte }; | ||
| 853 | }, | 882 | }, |
| 854 | .token_stored => |token| loc: { | 883 | .token_stored => |token| loc: { |
| 855 | const location = Tokenizer.tokenLocation(ast.utf8_source, 0, token.offset); | 884 | const location = Tokenizer.tokenLocation(ast.utf8_source, 0, token.offset); |
| ... | @@ -869,7 +898,7 @@ pub const Highlight = union(enum) { | ... | @@ -869,7 +898,7 @@ pub const Highlight = union(enum) { |
| 869 | n | 898 | n |
| 870 | else | 899 | else |
| 871 | (ast.utf8_source.len - b), | 900 | (ast.utf8_source.len - b), |
| 872 | else => comptime unreachable, | 901 | else => @compileError(unreachable), |
| 873 | }; | 902 | }; |
| 874 | break :loc .{ | 903 | break :loc .{ |
| 875 | .byte = b, | 904 | .byte = b, |
src/core/Sema.zig+3-3| ... | @@ -1328,7 +1328,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { | ... | @@ -1328,7 +1328,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { |
| 1328 | .sub => l - r, | 1328 | .sub => l - r, |
| 1329 | .mul => l * r, | 1329 | .mul => l * r, |
| 1330 | .div => l / r, | 1330 | .div => l / r, |
| 1331 | else => comptime unreachable, | 1331 | else => @compileError(unreachable), |
| 1332 | }; | 1332 | }; |
| 1333 | 1333 | ||
| 1334 | return try s.makeValue(if (left_ty == .numeric_literal) | 1334 | return try s.makeValue(if (left_ty == .numeric_literal) |
| ... | @@ -1346,7 +1346,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { | ... | @@ -1346,7 +1346,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { |
| 1346 | .sub => std.math.sub(i32, l, r), | 1346 | .sub => std.math.sub(i32, l, r), |
| 1347 | .mul => std.math.mul(i32, l, r), | 1347 | .mul => std.math.mul(i32, l, r), |
| 1348 | .div => return try s.makeValue(.{ .float = @as(f64, @floatFromInt(l)) / @as(f64, @floatFromInt(r)) }), | 1348 | .div => return try s.makeValue(.{ .float = @as(f64, @floatFromInt(l)) / @as(f64, @floatFromInt(r)) }), |
| 1349 | else => comptime unreachable, | 1349 | else => @compileError(unreachable), |
| 1350 | } catch @panic("TODO: out of range"); | 1350 | } catch @panic("TODO: out of range"); |
| 1351 | 1351 | ||
| 1352 | return try s.makeValue(.{ .int32 = result }); | 1352 | return try s.makeValue(.{ .int32 = result }); |
| ... | @@ -1362,7 +1362,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { | ... | @@ -1362,7 +1362,7 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { |
| 1362 | const result = switch (tag) { | 1362 | const result = switch (tag) { |
| 1363 | .lt => l < r, | 1363 | .lt => l < r, |
| 1364 | .gt => l > r, | 1364 | .gt => l > r, |
| 1365 | else => comptime unreachable, | 1365 | else => @compileError(unreachable), |
| 1366 | }; | 1366 | }; |
| 1367 | return @as( | 1367 | return @as( |
| 1368 | InternPool.Value, | 1368 | InternPool.Value, |
src/core/Tokenizer.zig+1-4| ... | @@ -612,9 +612,7 @@ fn tokenizeIdentifier(t: *Tokenizer, start_byte: u32) !Token { | ... | @@ -612,9 +612,7 @@ fn tokenizeIdentifier(t: *Tokenizer, start_byte: u32) !Token { |
| 612 | /// - May use 0x, 0b, 0o prefixes for hex, binary, octal numbers. | 612 | /// - May use 0x, 0b, 0o prefixes for hex, binary, octal numbers. |
| 613 | /// The token is tagged as `number_int` or `number_float` depending | 613 | /// The token is tagged as `number_int` or `number_float` depending |
| 614 | /// on if it contained a `.` This is to help later re-parses. | 614 | /// on if it contained a `.` This is to help later re-parses. |
| 615 | // | 615 | fn tokenizeNumber(t: *Tokenizer) !Token { |
| 616 | // TODO: is it a zig compiler bug that `?` is required here? | ||
| 617 | fn tokenizeNumber(t: *Tokenizer) !?Token { | ||
| 618 | const trace = T.trace(@src()); | 616 | const trace = T.trace(@src()); |
| 619 | defer trace.end(); | 617 | defer trace.end(); |
| 620 | 618 | ||
| ... | @@ -646,7 +644,6 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { | ... | @@ -646,7 +644,6 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { |
| 646 | t.pos += 2; | 644 | t.pos += 2; |
| 647 | try t.consumeIntegerRadix(.octal); | 645 | try t.consumeIntegerRadix(.octal); |
| 648 | }, | 646 | }, |
| 649 | '0' => @panic("TODO: no double zero"), | ||
| 650 | else => break :alt_base, | 647 | else => break :alt_base, |
| 651 | } | 648 | } |
| 652 | 649 |
src/core/Transform.zig+6-1| ... | @@ -797,8 +797,13 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -797,8 +797,13 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 797 | const value = switch (tag) { | 797 | const value = switch (tag) { |
| 798 | .number_int => std.fmt.parseFloat(f64, text) catch @panic("TODO: write my own float parser"), | 798 | .number_int => std.fmt.parseFloat(f64, text) catch @panic("TODO: write my own float parser"), |
| 799 | .number_float => std.fmt.parseFloat(f64, text) catch @panic("TODO: write my own float parser"), | 799 | .number_float => std.fmt.parseFloat(f64, text) catch @panic("TODO: write my own float parser"), |
| 800 | else => comptime unreachable, | 800 | else => @compileError(unreachable), |
| 801 | }; | 801 | }; |
| 802 | |||
| 803 | if (value != 0 and text[0] == '0' and (text.len == 1 or text[1] != '.')) { | ||
| 804 | try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) }); | ||
| 805 | } | ||
| 806 | |||
| 802 | return try t.addInstRef(.{ | 807 | return try t.addInstRef(.{ |
| 803 | .tag = .number, | 808 | .tag = .number, |
| 804 | .data = EncodedData.encode(.number, .{ | 809 | .data = EncodedData.encode(.number, .{ |