From 758f551db0e231fd29a9aeaa0b93ffad5c795fcc Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 16 Nov 2024 17:08:07 -0500 Subject: [PATCH] feat: structure for more number lexers --- src/01_lexic/number.zig | 91 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/src/01_lexic/number.zig b/src/01_lexic/number.zig index 7885b6f..f36a849 100644 --- a/src/01_lexic/number.zig +++ b/src/01_lexic/number.zig @@ -7,7 +7,55 @@ const TokenType = token.TokenType; const is_decimal_digit = utils.is_decimal_digit; -fn integer(input: []const u8, cap: usize, start: usize) !?Token { +const LexReturn = struct { Token, usize }; + +/// Attempts to lex a number, as per the language grammar. +/// +/// A number is either an Int or a Float. +/// No number can have a leading zero. That is an error to +/// avoid confussion with PHP literal octals. +fn lex(input: []const u8, cap: usize, start: usize) !?LexReturn { + const first_char = input[start]; + + // Attempt to lex a hex, octal or binary number + if (first_char == '0' and cap > start + 1) { + const second_char = input[start + 1]; + switch (second_char) { + 'x', 'X' => return hex(), + 'o', 'O' => return octal(), + 'b', 'B' => return binary(), + } + + // Leading zero found. Throw an error. + // TODO: throw an error :c + } + + // Attempt to lex an integer. + // Floating point numbers are lexed through the int lexer + return integer(input, cap, start); +} + +fn hex() !?LexReturn { + return null; +} + +fn octal() !?LexReturn { + return null; +} + +fn binary() !?LexReturn { + return null; +} + +/// Attempts to lex an integer number. +/// +/// This function fails if the first digit it encounters is a `0`, +/// this is because it could cause confusion with PHP literal integers, +/// where a number that starts with a `0` is octal, not decimal. +/// +/// For this reason, this function should be called after the lexers +/// for hex, octal and binary have been called. +fn integer(input: []const u8, cap: usize, start: usize) !?LexReturn { const first_char = input[start]; if (!is_decimal_digit(first_char)) { return null; @@ -18,16 +66,51 @@ fn integer(input: []const u8, cap: usize, start: usize) !?Token { last_pos += 1; } - return Token.init(input[start..last_pos], TokenType.Int, start); + return .{ + Token.init(input[start..last_pos], TokenType.Int, start), + last_pos, + }; } -test "number lexer" { +test "int lexer 1" { const input = "322 "; const result = try integer(input, input.len, 0); - if (result) |r| { + if (result) |tuple| { + const r = tuple[0]; try std.testing.expectEqualDeep("322", r.value); } else { try std.testing.expect(false); } } + +test "int lexer 2" { + const input = " 644 "; + const result = try integer(input, input.len, 3); + + if (result) |tuple| { + const r = tuple[0]; + try std.testing.expectEqualDeep("644", r.value); + } else { + try std.testing.expect(false); + } +} + +test "int lexer 3" { + const input = "4"; + const result = try integer(input, input.len, 0); + + if (result) |tuple| { + const r = tuple[0]; + try std.testing.expectEqualDeep("4", r.value); + } else { + try std.testing.expect(false); + } +} + +test "should return null if not an integer" { + const input = "prosor prosor"; + const result = try integer(input, input.len, 0); + + try std.testing.expect(result == null); +}