Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions tests/test_lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,28 @@ test_string_empty(void)
PASS();
}

static void
test_string_escaped_quote(void)
{
TEST("string literal with escaped quote");
wl_parser_lexer_t lex;
wl_parser_lexer_init(&lex, "\"a\\\"b\"");
ASSERT_TOK_STR(lex, WL_PARSER_LEXER_TOK_STRING, "a\"b");
ASSERT_TOK(lex, WL_PARSER_LEXER_TOK_EOF);
PASS();
}

static void
test_string_escaped_backslash(void)
{
TEST("string literal with escaped backslash");
wl_parser_lexer_t lex;
wl_parser_lexer_init(&lex, "\"a\\\\b\"");
ASSERT_TOK_STR(lex, WL_PARSER_LEXER_TOK_STRING, "a\\b");
ASSERT_TOK(lex, WL_PARSER_LEXER_TOK_EOF);
PASS();
}

/* ======================================================================== */
/* Lexer: Boolean Literals */
/* ======================================================================== */
Expand Down Expand Up @@ -881,6 +903,8 @@ main(void)
test_integer_large();
test_string_literal();
test_string_empty();
test_string_escaped_quote();
test_string_escaped_backslash();

printf("\n--- Boolean Literals ---\n");
test_true_literal();
Expand Down
48 changes: 48 additions & 0 deletions tests/test_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,52 @@ test_parse_fact_string_constant(void)
PASS();
}

static void
test_parse_fact_string_escaped_quote(void)
{
TEST("fact string constant with escaped quote");
PARSE("relation(\"s\", \"r\", \"a\\\"b\").");
ASSERT_PARSED();
const wl_parser_ast_node_t *fact = child(program, 0);
if (fact->type != WL_PARSER_AST_NODE_FACT || fact->child_count != 3) {
CLEANUP();
FAIL("expected relation fact with 3 args");
return;
}
const wl_parser_ast_node_t *arg2 = child(fact, 2);
if (arg2->type != WL_PARSER_AST_NODE_STRING || !arg2->str_value
|| strcmp(arg2->str_value, "a\"b") != 0) {
CLEANUP();
FAIL("arg 2 should be STRING 'a\"b'");
return;
}
CLEANUP();
PASS();
}

static void
test_parse_fact_string_escaped_backslash(void)
{
TEST("fact string constant with escaped backslash");
PARSE("path(\"a\\\\b\").");
ASSERT_PARSED();
const wl_parser_ast_node_t *fact = child(program, 0);
if (fact->type != WL_PARSER_AST_NODE_FACT || fact->child_count != 1) {
CLEANUP();
FAIL("expected path fact with 1 arg");
return;
}
const wl_parser_ast_node_t *arg0 = child(fact, 0);
if (arg0->type != WL_PARSER_AST_NODE_STRING || !arg0->str_value
|| strcmp(arg0->str_value, "a\\b") != 0) {
CLEANUP();
FAIL("arg 0 should be STRING 'a\\b'");
return;
}
CLEANUP();
PASS();
}

/* ======================================================================== */
/* Parser: Compound Terms (Issue #530) */
/* ======================================================================== */
Expand Down Expand Up @@ -1991,6 +2037,8 @@ main(void)
test_parse_facts_mixed_with_rules();
test_parse_fact_rejects_variables();
test_parse_fact_string_constant();
test_parse_fact_string_escaped_quote();
test_parse_fact_string_escaped_backslash();

printf("\n--- Compound Terms (Issue #530) ---\n");
test_parser_compound_basic();
Expand Down
23 changes: 20 additions & 3 deletions wirelog/parser/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ scan_string(wl_parser_lexer_t *lexer)
{
/* Opening quote already consumed */
while (!is_at_end(lexer) && peek(lexer) != '"') {
if (peek(lexer) == '\\'
&& (peek_next(lexer) == '"' || peek_next(lexer) == '\\')) {
advance(lexer);
advance(lexer);
continue;
}
advance(lexer);
}

Expand Down Expand Up @@ -649,13 +655,24 @@ char *
wl_parser_lexer_token_to_string(const wl_parser_lexer_token_t *token)
{
if (token->type == WL_PARSER_LEXER_TOK_STRING) {
/* Strip surrounding quotes */
/* Strip surrounding quotes and decode supported string escapes. */
if (token->length >= 2) {
uint32_t inner_len = token->length - 2;
char *str = (char *)malloc(inner_len + 1);
if (str) {
memcpy(str, token->start + 1, inner_len);
str[inner_len] = '\0';
const char *p = token->start + 1;
const char *end = p + inner_len;
char *out = str;
while (p < end) {
if (*p == '\\' && p + 1 < end
&& (p[1] == '"' || p[1] == '\\')) {
*out++ = p[1];
p += 2;
continue;
}
*out++ = *p++;
}
*out = '\0';
}
return str;
}
Expand Down
12 changes: 1 addition & 11 deletions wirelog/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,7 @@ token_to_name(const wl_parser_lexer_token_t *token)
static char *
token_to_str_value(const wl_parser_lexer_token_t *token)
{
/* Strip quotes from string token */
if (token->length >= 2) {
uint32_t inner = token->length - 2;
char *s = (char *)malloc(inner + 1);
if (s) {
memcpy(s, token->start + 1, inner);
s[inner] = '\0';
}
return s;
}
return (char *)calloc(1, 1);
return wl_parser_lexer_token_to_string(token);
}

static bool
Expand Down
Loading