From 91786a70ed2dbb003e12d6d33ca72593b70b6290 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 20 Jan 2024 16:41:13 -0500 Subject: Fix writing empty list objects within blank nodes --- NEWS | 3 ++- src/writer.c | 8 ++++++ test/test_writer.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 54f2db90..ad6ed098 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,9 @@ serd (0.32.1) unstable; urgency=medium * Enable clang nullability checks + * Fix writing empty list objects within blank nodes - -- David Robillard Tue, 09 Jan 2024 22:38:27 +0000 + -- David Robillard Sat, 20 Jan 2024 21:28:04 +0000 serd (0.32.0) stable; urgency=medium diff --git a/src/writer.c b/src/writer.c index bdcb8e8e..52badc46 100644 --- a/src/writer.c +++ b/src/writer.c @@ -964,6 +964,14 @@ serd_writer_write_statement(SerdWriter* writer, return SERD_ERR_BAD_ARG; } + if ((flags & SERD_LIST_O_BEGIN) && + !strcmp((const char*)object->buf, NS_RDF "nil")) { + /* Tolerate LIST_O_BEGIN for "()" objects, even though it doesn't make + much sense, because older versions handled this gracefully. Consider + making this an error in a later major version. */ + flags &= (SerdStatementFlags)~SERD_LIST_O_BEGIN; + } + // Simple case: write a line of NTriples or NQuads if (writer->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS) { TRY(st, write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags)); diff --git a/test/test_writer.c b/test/test_writer.c index 0622dc82..b02aba7e 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -42,6 +42,79 @@ test_write_long_literal(void) serd_free(out); } +static void +test_write_nested_anon(void) +{ + SerdEnv* env = serd_env_new(NULL); + SerdChunk chunk = {NULL, 0}; + SerdWriter* writer = serd_writer_new( + SERD_TURTLE, (SerdStyle)0, env, NULL, serd_chunk_sink, &chunk); + + assert(writer); + + SerdNode s0 = serd_node_from_string(SERD_URI, USTR("http://example.org/s0")); + SerdNode p0 = serd_node_from_string(SERD_URI, USTR("http://example.org/p0")); + SerdNode b0 = serd_node_from_string(SERD_BLANK, USTR("b0")); + SerdNode p1 = serd_node_from_string(SERD_URI, USTR("http://example.org/p1")); + SerdNode b1 = serd_node_from_string(SERD_BLANK, USTR("b1")); + SerdNode p2 = serd_node_from_string(SERD_URI, USTR("http://example.org/p2")); + SerdNode o2 = serd_node_from_string(SERD_URI, USTR("http://example.org/o2")); + SerdNode p3 = serd_node_from_string(SERD_URI, USTR("http://example.org/p3")); + SerdNode p4 = serd_node_from_string(SERD_URI, USTR("http://example.org/p4")); + SerdNode o4 = serd_node_from_string(SERD_URI, USTR("http://example.org/o4")); + SerdNode nil = serd_node_from_string( + SERD_URI, USTR("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil")); + + assert(!serd_writer_write_statement( + writer, SERD_ANON_O_BEGIN, NULL, &s0, &p0, &b0, NULL, NULL)); + + assert(!serd_writer_write_statement(writer, + SERD_ANON_O_BEGIN | SERD_ANON_CONT, + NULL, + &b0, + &p1, + &b1, + NULL, + NULL)); + + assert(!serd_writer_write_statement( + writer, SERD_ANON_CONT, NULL, &b1, &p2, &o2, NULL, NULL)); + + assert(!serd_writer_write_statement(writer, + SERD_ANON_CONT | SERD_LIST_O_BEGIN, + NULL, + &b1, + &p3, + &nil, + NULL, + NULL)); + + assert(!serd_writer_end_anon(writer, &b1)); + assert(!serd_writer_write_statement( + writer, SERD_ANON_CONT, NULL, &b0, &p4, &o4, NULL, NULL)); + + assert(!serd_writer_end_anon(writer, &b0)); + + serd_writer_free(writer); + serd_env_free(env); + + uint8_t* const out = serd_chunk_sink_finish(&chunk); + + static const char* const expected = + "\n" + "\t [\n" + "\t\t [\n" + "\t\t\t ;\n" + "\t\t\t ()\n" + "\t\t] ;\n" + "\t\t \n" + "\t] .\n"; + + fprintf(stderr, "%s\n", out); + assert(!strcmp((char*)out, expected)); + serd_free(out); +} + static size_t null_sink(const void* const buf, const size_t len, void* const stream) { @@ -158,6 +231,7 @@ int main(void) { test_write_long_literal(); + test_write_nested_anon(); test_writer_cleanup(); test_strict_write(); test_write_error(); -- cgit v1.2.1