diff --git a/src/01_lexic/number.zig b/src/01_lexic/number.zig index 98661b5..6f988e0 100644 --- a/src/01_lexic/number.zig +++ b/src/01_lexic/number.zig @@ -19,6 +19,7 @@ pub fn lex( cap: usize, start: usize, err: *errors.ErrorData, + alloc: std.mem.Allocator, ) LexError!?LexReturn { assert(start < cap); const first_char = input[start]; @@ -27,9 +28,9 @@ pub fn lex( if (first_char == '0' and cap > start + 1) { const second_char = input[start + 1]; switch (second_char) { - 'x', 'X' => return prefixed('x', input, cap, start, err), - 'o', 'O' => return prefixed('o', input, cap, start, err), - 'b', 'B' => return prefixed('b', input, cap, start, err), + 'x', 'X' => return prefixed('x', input, cap, start, err, alloc), + 'o', 'O' => return prefixed('o', input, cap, start, err, alloc), + 'b', 'B' => return prefixed('b', input, cap, start, err, alloc), else => { // Continue }, @@ -51,6 +52,7 @@ fn prefixed( cap: usize, start: usize, err: *errors.ErrorData, + alloc: std.mem.Allocator, ) !?LexReturn { const validator = switch (prefix) { 'x' => utils.is_hex_digit, @@ -64,7 +66,7 @@ fn prefixed( // There should be at least 1 valid digit if (end_position >= cap or !validator(input[end_position])) { // populate error information - err.init("Incomplete number", start, end_position); + try err.init("Incomplete number", start, end_position, alloc); // throw error return LexError.Incomplete; @@ -254,7 +256,7 @@ test "should return null if not an integer" { test "should lex hex number" { const input = "0xa"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -268,7 +270,7 @@ test "should fail on integer with leading zero" { const input = "0322"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.LeadingZero); return; }; @@ -285,7 +287,7 @@ test "should fail on integer with leading zero" { test "should lex hex number 2" { const input = " 0Xff00AA "; - const result = try lex(input, input.len, 2, undefined); + const result = try lex(input, input.len, 2, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -299,7 +301,7 @@ test "shouldnt parse incomplete hex number" { const input = "0xZZ"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.Incomplete); return; }; @@ -318,7 +320,7 @@ test "shouldnt parse incomplete hex number 2" { const input = "0x"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.Incomplete); return; }; @@ -335,7 +337,7 @@ test "shouldnt parse incomplete hex number 2" { test "should lex octal number" { const input = "0o755"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -347,7 +349,7 @@ test "should lex octal number" { test "should lex octal number 2" { const input = " 0o755 "; - const result = try lex(input, input.len, 2, undefined); + const result = try lex(input, input.len, 2, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -361,7 +363,7 @@ test "shouldnt parse incomplete octal number" { const input = "0o8"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.Incomplete); return; }; @@ -378,7 +380,7 @@ test "shouldnt parse incomplete octal number" { test "should lex binary number" { const input = "0b1011"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -392,7 +394,7 @@ test "shouldnt parse incomplete binary number" { const input = "0b2"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.Incomplete); return; }; @@ -409,7 +411,7 @@ test "shouldnt parse incomplete binary number" { test "should lex fp number 1" { const input = "1.2"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -423,7 +425,7 @@ test "should lex fp number 2" { const input = "0.1"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = try lex(input, input.len, 0, errdata); + const result = try lex(input, input.len, 0, errdata, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -435,7 +437,7 @@ test "should lex fp number 2" { test "should lex fp number 3" { const input = "123.456"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -449,7 +451,7 @@ test "should fail on incomplete fp number" { const input = "123."; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.IncompleteFloatingNumber); return; }; @@ -466,7 +468,7 @@ test "should fail on incomplete fp number" { test "should lex scientific number" { const input = "42e+3"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; @@ -480,7 +482,7 @@ test "should fail on incomplete scientific number" { const input = "123e"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.IncompleteScientificNumber); return; }; @@ -499,7 +501,7 @@ test "should fail on incomplete scientific number 2" { const input = "123e+"; const errdata = try std.testing.allocator.create(errors.ErrorData); defer std.testing.allocator.destroy(errdata); - const result = lex(input, input.len, 0, errdata) catch |err| { + const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| { try std.testing.expect(err == token.LexError.IncompleteScientificNumber); return; }; @@ -516,7 +518,7 @@ test "should fail on incomplete scientific number 2" { test "should lex floating scientific number" { const input = "0.58e+3"; - const result = try lex(input, input.len, 0, undefined); + const result = try lex(input, input.len, 0, undefined, std.testing.allocator); if (result) |tuple| { const r = tuple[0]; diff --git a/src/01_lexic/root.zig b/src/01_lexic/root.zig index b5ab1d1..28d282b 100644 --- a/src/01_lexic/root.zig +++ b/src/01_lexic/root.zig @@ -43,7 +43,7 @@ pub fn tokenize( // attempt to lex a number var current_error: errors.ErrorData = undefined; - const number_lex = number.lex(input, input_len, actual_next_pos, ¤t_error) catch |e| switch (e) { + const number_lex = number.lex(input, input_len, actual_next_pos, ¤t_error, alloc) catch |e| switch (e) { // recoverable errors LexError.Incomplete => { // add to list of errors @@ -126,7 +126,7 @@ pub fn tokenize( else { // Create an error "nothing matched" and continue lexing // after the whitespace - current_error.init("Unrecognized character", actual_next_pos, actual_next_pos + 1); + try current_error.init("Unrecognized character", actual_next_pos, actual_next_pos + 1, alloc); try err_arrl.append(current_error); current_pos = ignore_until_whitespace(input, actual_next_pos); continue; diff --git a/src/02_syntax/root.zig b/src/02_syntax/root.zig index 979adb8..562fd2f 100644 --- a/src/02_syntax/root.zig +++ b/src/02_syntax/root.zig @@ -48,10 +48,11 @@ pub const Module = struct { switch (e) { error.Unmatched => { // create the error value - error_target.init( + try error_target.init( "No statement found", current_pos, current_pos + 1, + allocator, ); return error.Unmatched; }, diff --git a/src/errors/root.zig b/src/errors/root.zig index 9bf184c..56bf9a3 100644 --- a/src/errors/root.zig +++ b/src/errors/root.zig @@ -1,22 +1,33 @@ const std = @import("std"); +pub const ErrorLabel = struct { + message: []const u8, + start: usize, + end: usize, +}; + /// Holds information about errors generated during the compilation, /// and pretty prints them. pub const ErrorData = struct { reason: []const u8, start_position: usize, end_position: usize, + labels: std.ArrayList(ErrorLabel), + alloc: std.mem.Allocator, pub fn init( target: *@This(), reason: []const u8, start_position: usize, end_position: usize, - ) void { + alloc: std.mem.Allocator, + ) !void { target.* = .{ .reason = reason, .start_position = start_position, .end_position = end_position, + .labels = std.ArrayList(ErrorLabel).init(alloc), + .alloc = alloc, }; } @@ -28,15 +39,11 @@ pub const ErrorData = struct { const error_message = try std.fmt.allocPrint(alloc, \\Error: {s} \\[{s}:{d}:{d}] - \\ - \\ {d} | {s} , .{ self.reason, filename, faulty_line.line_number, faulty_line.column_number, - faulty_line.line_number, - faulty_line.line, }); return error_message; @@ -47,9 +54,8 @@ pub const ErrorData = struct { // - Get previous, current and next line // - Display message - /// Does nothing at the moment pub fn deinit(self: *@This()) void { - _ = self; + self.labels.deinit(self.alloc); } }; @@ -150,6 +156,8 @@ test "should gen error message" { .reason = "Invalid identifier", .start_position = 6, .end_position = 9, + .labels = std.ArrayList(ErrorLabel).init(std.testing.allocator), + .alloc = std.testing.allocator, }; const out = try err.get_error_str(source, "repl", std.testing.allocator); defer std.testing.allocator.free(out); @@ -157,7 +165,5 @@ test "should gen error message" { try std.testing.expectEqualStrings( \\Error: Invalid identifier \\[repl:1:7] - \\ - \\ 1 | print(ehh) , out); }