From 71d617928a6038fbd1784c907f3e91facfc9224c Mon Sep 17 00:00:00 2001 From: Araozu Date: Wed, 27 Nov 2024 20:07:01 -0500 Subject: [PATCH] feat: lex escape characters inside strings --- src/01_lexic/string.zig | 90 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/src/01_lexic/string.zig b/src/01_lexic/string.zig index 1d673f3..b5ae7d1 100644 --- a/src/01_lexic/string.zig +++ b/src/01_lexic/string.zig @@ -19,21 +19,43 @@ pub fn lex(input: []const u8, start: usize) LexError!?LexReturn { // lex everything but quote and newline // TODO: escape characters + var current_pos = start + 1; - while (current_pos < cap and input[current_pos] != '"' and input[current_pos] != '\n') { + + while (current_pos < cap) { + const next_char = input[current_pos]; + // string is finished, return it + if (next_char == '"') { + return .{ + Token.init(input[start .. current_pos + 1], TokenType.String, start), + current_pos + 1, + }; + } + // new line, return error + else if (next_char == '\n') { + return LexError.IncompleteString; + } + // lex escape characters + else if (next_char == '\\') { + // if next char is EOF, return error + if (current_pos + 1 == cap) { + return LexError.IncompleteString; + } + // if next char is newline, return error + else if (input[current_pos + 1] == '\n') { + return LexError.IncompleteString; + } + // here just consume whatever char is after + // TODO: if next char is not an escape char, return warning? + current_pos += 2; + continue; + } + current_pos += 1; } - // expect ending quote - if (current_pos == cap or input[current_pos] == '\n') { - // Error: EOF before ending the string - return LexError.IncompleteString; - } else { - return .{ - Token.init(input[start .. current_pos + 1], TokenType.String, start), - current_pos + 1, - }; - } + // this could only reach when EOF is hit, return error + return LexError.IncompleteString; } test "should lex empty string" { @@ -100,3 +122,49 @@ test "should fail on newline before closing string" { try std.testing.expect(false); } + +test "should lex string with escape character 1" { + const input = "\"test\\\"string\""; + const output = try lex(input, 0); + + if (output) |tuple| { + const t = tuple[0]; + try std.testing.expectEqualDeep("\"test\\\"string\"", t.value); + try std.testing.expectEqual(14, tuple[1]); + } else { + try std.testing.expect(false); + } +} + +test "should lex string with escape character 2" { + const input = "\"test\\\\string\""; + const output = try lex(input, 0); + + if (output) |tuple| { + const t = tuple[0]; + try std.testing.expectEqualDeep("\"test\\\\string\"", t.value); + try std.testing.expectEqual(14, tuple[1]); + } else { + try std.testing.expect(false); + } +} + +test "should fail on EOF after backslash" { + const input = "\"hello \\"; + _ = lex(input, 0) catch |err| { + try std.testing.expectEqual(LexError.IncompleteString, err); + return; + }; + + try std.testing.expect(false); +} + +test "should fail on newline after backslash" { + const input = "\"hello \\\n"; + _ = lex(input, 0) catch |err| { + try std.testing.expectEqual(LexError.IncompleteString, err); + return; + }; + + try std.testing.expect(false); +}