From 76c823cc543f3d119856b92875a285265677c64d Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 16 Nov 2024 18:56:12 -0500 Subject: [PATCH] feat: use comptime function to lex hex, octal and binary numbers --- src/01_lexic/number.zig | 102 ++++++++++++++++++++++++++++++++++------ src/01_lexic/utils.zig | 8 ++++ 2 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/01_lexic/number.zig b/src/01_lexic/number.zig index 4df12bd..7646733 100644 --- a/src/01_lexic/number.zig +++ b/src/01_lexic/number.zig @@ -22,12 +22,11 @@ pub fn lex(input: []const u8, cap: usize, start: usize) LexError!?LexReturn { if (first_char == '0' and cap > start + 1) { const second_char = input[start + 1]; switch (second_char) { - 'x', 'X' => return hex(input, cap, start), - 'o', 'O' => return octal(), - 'b', 'B' => return binary(), + 'x', 'X' => return prefixed('x', input, cap, start), + 'o', 'O' => return prefixed('o', input, cap, start), + 'b', 'B' => return prefixed('b', input, cap, start), else => { // Leading zero found. Throw an error. - // TODO: throw an error :c return LexError.LeadingZero; }, } @@ -38,21 +37,28 @@ pub fn lex(input: []const u8, cap: usize, start: usize) LexError!?LexReturn { return integer(input, cap, start); } -/// Lexes a hexadecimal number. -/// Allows 0-9a-fA-F -/// Assumes that `start` is the position of the initial zero -fn hex(input: []const u8, cap: usize, start: usize) LexError!?LexReturn { +/// comptime function for lexing hex, octal and binary numbers. +/// only allowed values for `prefix` are `x`, `o` & `b`. +/// An adequate validator is choosen based on `prefix`, +/// that validator will decide which characters to lex. +fn prefixed(comptime prefix: u8, input: []const u8, cap: usize, start: usize) !?LexReturn { + const validator = switch (prefix) { + 'x' => utils.is_hex_digit, + 'o' => utils.is_octal_digit, + 'b' => utils.is_binary_digit, + else => @compileError("Invalid prefix passed to `prefixed` function."), + }; + var end_position = start + 2; // There should be at least 1 hex digit - if (end_position >= cap or !utils.is_hex_digit(input[end_position])) { + if (end_position >= cap or !validator(input[end_position])) { return LexError.Incomplete; } // loop through all chars end_position += 1; - - while (end_position < cap and utils.is_hex_digit(input[end_position])) { + while (end_position < cap and validator(input[end_position])) { end_position += 1; } @@ -62,10 +68,6 @@ fn hex(input: []const u8, cap: usize, start: usize) LexError!?LexReturn { }; } -fn octal() !?LexReturn { - return null; -} - fn binary() !?LexReturn { return null; } @@ -195,3 +197,73 @@ test "shouldnt parse incomplete hex number 2" { try std.testing.expect(false); } + +test "should lex octal number" { + const input = "0o755"; + const result = try lex(input, input.len, 0); + + if (result) |tuple| { + const r = tuple[0]; + try std.testing.expectEqualDeep("0o755", r.value); + } else { + try std.testing.expect(false); + } +} + +test "should lex octal number 2" { + const input = " 0o755 "; + const result = try lex(input, input.len, 2); + + if (result) |tuple| { + const r = tuple[0]; + try std.testing.expectEqualDeep("0o755", r.value); + } else { + try std.testing.expect(false); + } +} + +test "shouldnt parse incomplete octal number" { + const input = "0o8"; + const result = lex(input, input.len, 0) catch |err| { + try std.testing.expect(err == token.LexError.Incomplete); + return; + }; + + if (result) |tuple| { + const r = tuple[0]; + std.debug.print("{s}\n", .{r.value}); + } else { + std.debug.print("nil returned", .{}); + } + + try std.testing.expect(false); +} + +test "should lex binary number" { + const input = "0b1011"; + const result = try lex(input, input.len, 0); + + if (result) |tuple| { + const r = tuple[0]; + try std.testing.expectEqualDeep("0b1011", r.value); + } else { + try std.testing.expect(false); + } +} + +test "shouldnt parse incomplete binary number" { + const input = "0b2"; + const result = lex(input, input.len, 0) catch |err| { + try std.testing.expect(err == token.LexError.Incomplete); + return; + }; + + if (result) |tuple| { + const r = tuple[0]; + std.debug.print("{s}\n", .{r.value}); + } else { + std.debug.print("nil returned", .{}); + } + + try std.testing.expect(false); +} diff --git a/src/01_lexic/utils.zig b/src/01_lexic/utils.zig index 0a4760d..71fa433 100644 --- a/src/01_lexic/utils.zig +++ b/src/01_lexic/utils.zig @@ -2,6 +2,14 @@ pub fn is_decimal_digit(c: u8) bool { return '0' <= c and c <= '9'; } +pub fn is_octal_digit(c: u8) bool { + return '0' <= c and c <= '7'; +} + +pub fn is_binary_digit(c: u8) bool { + return c == '0' or c == '1'; +} + pub fn is_hex_digit(c: u8) bool { return ('0' <= c and c <= '9') or ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F'); }