From 672e90382da08efa8f593fdc9081e31d0e548fa0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 14 Aug 2020 16:05:10 +0200 Subject: Merge datatype/language into node This moves closer to the sord API, and is more convenient in most cases. --- include/serd/node.h | 37 +++++++++++++- include/serd/sink.h | 15 +++--- include/serd/writer.h | 4 +- src/n3.c | 33 +++++++------ src/node.c | 119 ++++++++++++++++++++++++++++++++++++++++++++-- src/reader.c | 10 +--- src/reader.h | 2 +- src/writer.c | 80 ++++++++++++++----------------- test/test_node.c | 46 ++++++++++++++++++ test/test_reader_writer.c | 58 ++++++---------------- test/test_writer.c | 18 ++++--- 11 files changed, 282 insertions(+), 140 deletions(-) diff --git a/include/serd/node.h b/include/serd/node.h index b95891c9..4aa4ec0a 100644 --- a/include/serd/node.h +++ b/include/serd/node.h @@ -84,8 +84,10 @@ typedef enum { /// Node flags, which ORed together make a #SerdNodeFlags typedef enum { - SERD_HAS_NEWLINE = 1U << 0U, ///< Contains line breaks ('\\n' or '\\r') - SERD_HAS_QUOTE = 1U << 1U, ///< Contains quotes ('"') + SERD_HAS_NEWLINE = 1U << 0U, ///< Contains line breaks ('\\n' or '\\r') + SERD_HAS_QUOTE = 1U << 1U, ///< Contains quotes ('"') + SERD_HAS_DATATYPE = 1U << 2U, ///< Literal node has datatype + SERD_HAS_LANGUAGE = 1U << 3U, ///< Literal node has language } SerdNodeFlag; /// Bitwise OR of #SerdNodeFlag values @@ -111,6 +113,17 @@ serd_new_substring(SerdNodeType type, const char* SERD_NULLABLE str, size_t len); +/** + Create a new literal node from `str`. + + Either `datatype` or `lang` can be given, but not both, unless `datatype` is + rdf:langString in which case it is ignored. +*/ +SERD_API SerdNode* SERD_ALLOCATED +serd_new_literal(const char* SERD_NONNULL str, + const char* SERD_NULLABLE datatype, + const char* SERD_NULLABLE lang); + /** Create a new URI node from a parsed URI. */ @@ -227,6 +240,26 @@ serd_node_string_view(const SerdNode* SERD_NONNULL node); SERD_API SerdURIView serd_node_uri_view(const SerdNode* SERD_NONNULL node); +/** + Return the optional datatype of a literal node. + + The datatype, if present, is always a URI, typically something like + . +*/ +SERD_PURE_API const SerdNode* SERD_NULLABLE +serd_node_datatype(const SerdNode* SERD_NONNULL node); + +/** + Return the optional language tag of a literal node. + + The language tag, if present, is a well-formed BCP 47 (RFC 4647) language + tag like "en-ca". Note that these must be handled case-insensitively, for + example, the common form "en-CA" is valid, but lowercase is considered + canonical here. +*/ +SERD_PURE_API const SerdNode* SERD_NULLABLE +serd_node_language(const SerdNode* SERD_NONNULL node); + /** @} @defgroup serd_node_operators Operators diff --git a/include/serd/sink.h b/include/serd/sink.h index 97b47a2f..702197d5 100644 --- a/include/serd/sink.h +++ b/include/serd/sink.h @@ -39,15 +39,12 @@ typedef SerdStatus (*SerdPrefixFunc)(void* SERD_NULLABLE handle, Called for every RDF statement in the serialisation. */ -typedef SerdStatus (*SerdStatementFunc)( - void* SERD_NULLABLE handle, - SerdStatementFlags flags, - const SerdNode* SERD_NULLABLE graph, - const SerdNode* SERD_NONNULL subject, - const SerdNode* SERD_NONNULL predicate, - const SerdNode* SERD_NONNULL object, - const SerdNode* SERD_NULLABLE object_datatype, - const SerdNode* SERD_NULLABLE object_lang); +typedef SerdStatus (*SerdStatementFunc)(void* SERD_NULLABLE handle, + SerdStatementFlags flags, + const SerdNode* SERD_NULLABLE graph, + const SerdNode* SERD_NONNULL subject, + const SerdNode* SERD_NONNULL predicate, + const SerdNode* SERD_NONNULL object); /** Sink function for anonymous node end markers. diff --git a/include/serd/writer.h b/include/serd/writer.h index 66c3d338..3327c1b9 100644 --- a/include/serd/writer.h +++ b/include/serd/writer.h @@ -139,9 +139,7 @@ serd_writer_write_statement(SerdWriter* SERD_NONNULL writer, const SerdNode* SERD_NULLABLE graph, const SerdNode* SERD_NONNULL subject, const SerdNode* SERD_NONNULL predicate, - const SerdNode* SERD_NONNULL object, - const SerdNode* SERD_NULLABLE datatype, - const SerdNode* SERD_NULLABLE lang); + const SerdNode* SERD_NONNULL object); /** Mark the end of an anonymous node's description. diff --git a/src/n3.c b/src/n3.c index c0c00985..929b1a97 100644 --- a/src/n3.c +++ b/src/n3.c @@ -842,10 +842,11 @@ read_0_9(SerdReader* const reader, const Ref str, const bool at_least_one) } static SerdStatus -read_number(SerdReader* const reader, - Ref* const dest, - Ref* const datatype, - bool* const ate_dot) +read_number(SerdReader* const reader, + Ref* const dest, + Ref* const datatype, + SerdNodeFlags* const flags, + bool* const ate_dot) { #define XSD_DECIMAL NS_XSD "decimal" #define XSD_DOUBLE NS_XSD "double" @@ -904,6 +905,10 @@ read_number(SerdReader* const reader, push_node(reader, SERD_URI, XSD_INTEGER, sizeof(XSD_INTEGER) - 1); } + if (*datatype) { + *flags |= SERD_HAS_DATATYPE; + } + return SERD_SUCCESS; } @@ -938,6 +943,7 @@ read_literal(SerdReader* const reader, switch (peek_byte(reader)) { case '@': skip_byte(reader, '@'); + *flags |= SERD_HAS_LANGUAGE; if ((st = read_LANGTAG(reader, lang))) { *datatype = pop_node(reader, *datatype); *lang = pop_node(reader, *lang); @@ -951,6 +957,7 @@ read_literal(SerdReader* const reader, return r_err(reader, SERD_BAD_SYNTAX, "expected '^'\n"); } + *flags |= SERD_HAS_DATATYPE; if ((st = read_iri(reader, datatype, ate_dot))) { *datatype = pop_node(reader, *datatype); *lang = pop_node(reader, *lang); @@ -959,7 +966,6 @@ read_literal(SerdReader* const reader, } break; } - return SERD_SUCCESS; } @@ -1076,7 +1082,7 @@ read_anon(SerdReader* const reader, // Emit statement with this anonymous object first SerdStatus st = SERD_SUCCESS; if (ctx.subject) { - TRY(st, emit_statement(reader, ctx, *dest, 0, 0)); + TRY(st, emit_statement(reader, ctx, *dest)); } // Switch the subject to the anonymous node and read its description @@ -1086,19 +1092,16 @@ read_anon(SerdReader* const reader, if (!subject) { *ctx.flags |= SERD_ANON_CONT; } - bool ate_dot_in_list = false; TRY_FAILING(st, read_predicateObjectList(reader, ctx, &ate_dot_in_list)); if (ate_dot_in_list) { return r_err(reader, SERD_BAD_SYNTAX, "'.' inside blank\n"); } - read_ws_star(reader); if (reader->end_func) { reader->end_func(reader->handle, deref(reader, *dest)); } - *ctx.flags = old_flags; } @@ -1175,7 +1178,7 @@ read_object(SerdReader* const reader, case '7': case '8': case '9': - st = read_number(reader, &o, &datatype, ate_dot); + st = read_number(reader, &o, &datatype, &flags, ate_dot); break; case '\"': case '\'': @@ -1191,6 +1194,7 @@ read_object(SerdReader* const reader, node = deref(reader, o); if ((node->length == 4 && !memcmp(serd_node_string(node), "true", 4)) || (node->length == 5 && !memcmp(serd_node_string(node), "false", 5))) { + flags = flags | SERD_HAS_DATATYPE; node->type = SERD_LITERAL; datatype = push_node(reader, SERD_URI, XSD_BOOLEAN, XSD_BOOLEAN_LEN); st = SERD_SUCCESS; @@ -1210,7 +1214,7 @@ read_object(SerdReader* const reader, } if (!st && emit && simple) { - st = emit_statement(reader, *ctx, o, datatype, lang); + st = emit_statement(reader, *ctx, o); } else if (!st && !emit) { ctx->object = o; ctx->datatype = datatype; @@ -1305,6 +1309,7 @@ static SerdStatus read_collection(SerdReader* const reader, ReadContext ctx, Ref* const dest) { SerdStatus st = SERD_SUCCESS; + skip_byte(reader, '('); bool end = peek_delim(reader, ')'); @@ -1312,7 +1317,7 @@ read_collection(SerdReader* const reader, ReadContext ctx, Ref* const dest) *dest = end ? reader->rdf_nil : blank_id(reader); if (ctx.subject) { // Reading a collection object *ctx.flags |= (end ? 0 : SERD_LIST_O_BEGIN); - TRY(st, emit_statement(reader, ctx, *dest, 0, 0)); + TRY(st, emit_statement(reader, ctx, *dest)); *ctx.flags &= SERD_LIST_O_BEGIN; *ctx.flags |= SERD_LIST_CONT; } else { // Reading a collection subject @@ -1352,7 +1357,7 @@ read_collection(SerdReader* const reader, ReadContext ctx, Ref* const dest) // _:node rdf:rest _:rest *ctx.flags |= SERD_LIST_CONT; ctx.predicate = reader->rdf_rest; - TRY(st, emit_statement(reader, ctx, (end ? reader->rdf_nil : rest), 0, 0)); + TRY(st, emit_statement(reader, ctx, (end ? reader->rdf_nil : rest))); ctx.subject = rest; // _:node = _:rest rest = node; // _:rest = (old)_:node @@ -1745,7 +1750,7 @@ read_nquads_statement(SerdReader* const reader) } } - TRY(st, emit_statement(reader, ctx, ctx.object, ctx.datatype, ctx.lang)); + TRY(st, emit_statement(reader, ctx, ctx.object)); pop_node(reader, ctx.graph); pop_node(reader, ctx.lang); diff --git a/src/node.c b/src/node.c index de42075e..8edf7fc1 100644 --- a/src/node.c +++ b/src/node.c @@ -34,6 +34,8 @@ static const size_t serd_node_align = 2 * sizeof(uint64_t); +static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE); + static size_t serd_uri_string_length(const SerdURIView* const uri) { @@ -69,10 +71,24 @@ serd_node_pad_size(const size_t n_bytes) return n_bytes + 2 + pad; } +static const SerdNode* +serd_node_meta_c(const SerdNode* const node) +{ + return node + 1 + (serd_node_pad_size(node->length) / sizeof(SerdNode)); +} + +static const SerdNode* +serd_node_maybe_get_meta_c(const SerdNode* const node) +{ + return (node->flags & meta_mask) ? serd_node_meta_c(node) : NULL; +} + static SERD_PURE_FUNC size_t serd_node_total_size(const SerdNode* const node) { - return node ? (sizeof(SerdNode) + serd_node_pad_size(node->length)) : 0; + return node ? (sizeof(SerdNode) + serd_node_pad_size(node->length) + + serd_node_total_size(serd_node_maybe_get_meta_c(node))) + : 0; } SerdNode* @@ -87,6 +103,7 @@ serd_node_malloc(const size_t length, node->flags = flags; node->type = type; + assert((uintptr_t)node % serd_node_align == 0U); return node; } @@ -103,7 +120,7 @@ serd_node_set(SerdNode** const dst, const SerdNode* const src) } assert(*dst); - memcpy(*dst, src, sizeof(SerdNode) + src->length + 1); + memcpy(*dst, src, size); } SerdNode* @@ -112,8 +129,10 @@ serd_new_string(SerdNodeType type, const char* str) SerdNodeFlags flags = 0; const size_t length = serd_strlen(str, &flags); SerdNode* node = serd_node_malloc(length, flags, type); + memcpy(serd_node_buffer(node), str, length); node->length = length; + return node; } @@ -130,6 +149,49 @@ serd_new_substring(const SerdNodeType type, return node; } +SerdNode* +serd_new_literal(const char* const str, + const char* const datatype, + const char* const lang) +{ + SerdNodeFlags flags = 0; + const size_t length = serd_strlen(str, &flags); + const size_t len = serd_node_pad_size(length); + + SerdNode* node = NULL; + if (lang) { + flags |= SERD_HAS_LANGUAGE; + const size_t lang_len = strlen(lang); + const size_t total_len = len + sizeof(SerdNode) + lang_len; + node = serd_node_malloc(total_len, flags, SERD_LITERAL); + memcpy(serd_node_buffer(node), str, length); + node->length = length; + + SerdNode* lang_node = node + 1 + (len / sizeof(SerdNode)); + lang_node->type = SERD_LITERAL; + lang_node->length = lang_len; + memcpy(serd_node_buffer(lang_node), lang, lang_len); + } else if (datatype) { + flags |= SERD_HAS_DATATYPE; + const size_t datatype_len = strlen(datatype); + const size_t total_len = len + sizeof(SerdNode) + datatype_len; + node = serd_node_malloc(total_len, flags, SERD_LITERAL); + memcpy(serd_node_buffer(node), str, length); + node->length = length; + + SerdNode* datatype_node = node + 1 + (len / sizeof(SerdNode)); + datatype_node->type = SERD_URI; + datatype_node->length = datatype_len; + memcpy(serd_node_buffer(datatype_node), datatype, datatype_len); + } else { + node = serd_node_malloc(length, flags, SERD_LITERAL); + memcpy(serd_node_buffer(node), str, length); + node->length = length; + } + + return node; +} + SerdNode* serd_node_copy(const SerdNode* node) { @@ -146,9 +208,30 @@ serd_node_copy(const SerdNode* node) bool serd_node_equals(const SerdNode* const a, const SerdNode* const b) { - return (a == b) || - (a && b && a->type == b->type && a->length == b->length && - !memcmp(serd_node_string(a), serd_node_string(b), a->length)); + if (a == b) { + return true; + } + + if (!a || !b || a->length != b->length || a->flags != b->flags || + a->type != b->type) { + return false; + } + + const size_t length = a->length; + if (!!memcmp(serd_node_string(a), serd_node_string(b), length)) { + return false; + } + + const SerdNodeFlags flags = a->flags; + if (flags & meta_mask) { + const SerdNode* const am = serd_node_meta_c(a); + const SerdNode* const bm = serd_node_meta_c(b); + + return am->length == bm->length && am->type == bm->type && + !memcmp(serd_node_string(am), serd_node_string(bm), am->length); + } + + return true; } SerdNode* @@ -444,6 +527,32 @@ serd_node_uri_view(const SerdNode* const node) : SERD_URI_NULL; } +const SerdNode* +serd_node_datatype(const SerdNode* const node) +{ + if (!node || !(node->flags & SERD_HAS_DATATYPE)) { + return NULL; + } + + const size_t len = serd_node_pad_size(node->length); + const SerdNode* const datatype = node + 1 + (len / sizeof(SerdNode)); + assert(datatype->type == SERD_URI || datatype->type == SERD_CURIE); + return datatype; +} + +const SerdNode* +serd_node_language(const SerdNode* const node) +{ + if (!node || !(node->flags & SERD_HAS_LANGUAGE)) { + return NULL; + } + + const size_t len = serd_node_pad_size(node->length); + const SerdNode* const lang = node + 1 + (len / sizeof(SerdNode)); + assert(lang->type == SERD_LITERAL); + return lang; +} + SerdNodeFlags serd_node_flags(const SerdNode* const node) { diff --git a/src/reader.c b/src/reader.c index 140c07ba..02738cd2 100644 --- a/src/reader.c +++ b/src/reader.c @@ -136,11 +136,7 @@ pop_node(SerdReader* const reader, const Ref ref) } SerdStatus -emit_statement(SerdReader* const reader, - const ReadContext ctx, - const Ref o, - const Ref d, - const Ref l) +emit_statement(SerdReader* const reader, const ReadContext ctx, const Ref o) { SerdNode* graph = deref(reader, ctx.graph); if (!graph && reader->default_graph) { @@ -154,9 +150,7 @@ emit_statement(SerdReader* const reader, graph, deref(reader, ctx.subject), deref(reader, ctx.predicate), - deref(reader, o), - deref(reader, d), - deref(reader, l)); + deref(reader, o)); *ctx.flags &= SERD_ANON_CONT | SERD_LIST_CONT; // Preserve only cont flags return st; diff --git a/src/reader.h b/src/reader.h index a528fcc8..0de1df2a 100644 --- a/src/reader.h +++ b/src/reader.h @@ -105,7 +105,7 @@ Ref pop_node(SerdReader* reader, Ref ref); SerdStatus -emit_statement(SerdReader* reader, ReadContext ctx, Ref o, Ref d, Ref l); +emit_statement(SerdReader* reader, ReadContext ctx, Ref o); SerdStatus read_n3_statement(SerdReader* reader); diff --git a/src/writer.c b/src/writer.c index a0325243..5aad6d29 100644 --- a/src/writer.c +++ b/src/writer.c @@ -143,8 +143,6 @@ typedef enum { RESET_GRAPH = 1U << 0U, RESET_INDENT = 1U << 1U } ResetFlag; SERD_NODISCARD static SerdStatus write_node(SerdWriter* writer, const SerdNode* node, - const SerdNode* datatype, - const SerdNode* lang, Field field, SerdStatementFlags flags); @@ -667,15 +665,15 @@ reset_context(SerdWriter* writer, const unsigned flags) } SERD_NODISCARD static SerdStatus -write_literal(SerdWriter* writer, - const SerdNode* node, - const SerdNode* datatype, - const SerdNode* lang, - SerdStatementFlags flags) +write_literal(SerdWriter* const writer, + const SerdNode* const node, + const SerdStatementFlags flags) { - SerdStatus st = SERD_SUCCESS; - const char* const node_str = serd_node_string(node); - const char* const type_uri = datatype ? serd_node_string(datatype) : NULL; + SerdStatus st = SERD_SUCCESS; + const SerdNode* const datatype = serd_node_datatype(node); + const SerdNode* const lang = serd_node_language(node); + const char* const node_str = serd_node_string(node); + const char* const type_uri = datatype ? serd_node_string(datatype) : NULL; if (supports_abbrev(writer) && type_uri) { if (!strncmp(type_uri, NS_XSD, sizeof(NS_XSD) - 1) && @@ -710,7 +708,7 @@ write_literal(SerdWriter* writer, TRY(st, esink(serd_node_string(lang), lang->length, writer)); } else if (type_uri) { TRY(st, esink("^^", 2, writer)); - return write_node(writer, datatype, NULL, NULL, FIELD_NONE, flags); + return write_node(writer, datatype, FIELD_NONE, flags); } return st; @@ -862,18 +860,16 @@ write_blank(SerdWriter* const writer, } SERD_NODISCARD static SerdStatus -write_node(SerdWriter* writer, - const SerdNode* node, - const SerdNode* datatype, - const SerdNode* lang, - Field field, - SerdStatementFlags flags) +write_node(SerdWriter* const writer, + const SerdNode* const node, + const Field field, + const SerdStatementFlags flags) { SerdStatus st = SERD_SUCCESS; switch (node->type) { case SERD_LITERAL: - st = write_literal(writer, node, datatype, lang, flags); + st = write_literal(writer, node, flags); break; case SERD_URI: st = write_uri_node(writer, node, field); @@ -904,7 +900,7 @@ write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred) { SerdStatus st = SERD_SUCCESS; - TRY(st, write_node(writer, pred, NULL, NULL, FIELD_PREDICATE, flags)); + TRY(st, write_node(writer, pred, FIELD_PREDICATE, flags)); TRY(st, write_sep(writer, SEP_P_O)); serd_node_set(&writer->context.predicate, pred); @@ -914,12 +910,10 @@ write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred) } SERD_NODISCARD static SerdStatus -write_list_next(SerdWriter* writer, - SerdStatementFlags flags, - const SerdNode* predicate, - const SerdNode* object, - const SerdNode* datatype, - const SerdNode* lang) +write_list_next(SerdWriter* const writer, + const SerdStatementFlags flags, + const SerdNode* const predicate, + const SerdNode* const object) { SerdStatus st = SERD_SUCCESS; @@ -929,7 +923,7 @@ write_list_next(SerdWriter* writer, } if (!strcmp(serd_node_string(predicate), NS_RDF "first")) { - TRY(st, write_node(writer, object, datatype, lang, FIELD_OBJECT, flags)); + TRY(st, write_node(writer, object, FIELD_OBJECT, flags)); } else { TRY(st, write_sep(writer, SEP_LIST_SEP)); } @@ -954,14 +948,12 @@ terminate_context(SerdWriter* writer) } SerdStatus -serd_writer_write_statement(SerdWriter* writer, - SerdStatementFlags flags, - const SerdNode* graph, - const SerdNode* subject, - const SerdNode* predicate, - const SerdNode* object, - const SerdNode* datatype, - const SerdNode* lang) +serd_writer_write_statement(SerdWriter* const writer, + const SerdStatementFlags flags, + const SerdNode* const graph, + const SerdNode* const subject, + const SerdNode* const predicate, + const SerdNode* const object) { SerdStatus st = SERD_SUCCESS; @@ -971,14 +963,14 @@ serd_writer_write_statement(SerdWriter* writer, // 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)); + TRY(st, write_node(writer, subject, FIELD_SUBJECT, flags)); TRY(st, esink(" ", 1, writer)); - TRY(st, write_node(writer, predicate, NULL, NULL, FIELD_PREDICATE, flags)); + TRY(st, write_node(writer, predicate, FIELD_PREDICATE, flags)); TRY(st, esink(" ", 1, writer)); - TRY(st, write_node(writer, object, datatype, lang, FIELD_OBJECT, flags)); + TRY(st, write_node(writer, object, FIELD_OBJECT, flags)); if (writer->syntax == SERD_NQUADS && graph) { TRY(st, esink(" ", 1, writer)); - TRY(st, write_node(writer, graph, datatype, lang, FIELD_GRAPH, flags)); + TRY(st, write_node(writer, graph, FIELD_GRAPH, flags)); } TRY(st, esink(" .\n", 3, writer)); return SERD_SUCCESS; @@ -991,7 +983,7 @@ serd_writer_write_statement(SerdWriter* writer, reset_context(writer, RESET_GRAPH | RESET_INDENT); if (graph) { TRY(st, write_newline(writer)); - TRY(st, write_node(writer, graph, datatype, lang, FIELD_GRAPH, flags)); + TRY(st, write_node(writer, graph, FIELD_GRAPH, flags)); TRY(st, write_sep(writer, SEP_GRAPH_BEGIN)); serd_node_set(&writer->context.graph, graph); } @@ -1004,9 +996,7 @@ serd_writer_write_statement(SerdWriter* writer, return esink("()", 2, writer); } - TRY_FAILING( - st, write_list_next(writer, flags, predicate, object, datatype, lang)); - + TRY_FAILING(st, write_list_next(writer, flags, predicate, object)); if (st == SERD_FAILURE) { // Reached end of list pop_context(writer); return SERD_SUCCESS; @@ -1040,7 +1030,7 @@ serd_writer_write_statement(SerdWriter* writer, TRY(st, write_pred(writer, flags, predicate)); } - TRY(st, write_node(writer, object, datatype, lang, FIELD_OBJECT, flags)); + TRY(st, write_node(writer, object, FIELD_OBJECT, flags)); } else { // No abbreviation @@ -1053,7 +1043,7 @@ serd_writer_write_statement(SerdWriter* writer, TRY(st, write_newline(writer)); } - TRY(st, write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags)); + TRY(st, write_node(writer, subject, FIELD_SUBJECT, flags)); if ((flags & (SERD_ANON_S_BEGIN | SERD_LIST_S_BEGIN))) { TRY(st, write_sep(writer, SEP_ANON_S_P)); } else { @@ -1071,7 +1061,7 @@ serd_writer_write_statement(SerdWriter* writer, TRY(st, write_pred(writer, flags, predicate)); } - TRY(st, write_node(writer, object, datatype, lang, FIELD_OBJECT, flags)); + TRY(st, write_node(writer, object, FIELD_OBJECT, flags)); } if (flags & (SERD_ANON_S_BEGIN | SERD_LIST_S_BEGIN)) { diff --git a/test/test_node.c b/test/test_node.c index 11351e31..1d4d1107 100644 --- a/test/test_node.c +++ b/test/test_node.c @@ -196,6 +196,51 @@ test_node_from_substring(void) serd_node_free(a_b); } +static void +check_copy_equals(const SerdNode* const node) +{ + SerdNode* const copy = serd_node_copy(node); + + assert(serd_node_equals(node, copy)); + + serd_node_free(copy); +} + +static void +test_literal(void) +{ + SerdNode* hello2 = serd_new_literal("hello\"", NULL, NULL); + assert(serd_node_length(hello2) == 6 && + serd_node_flags(hello2) == SERD_HAS_QUOTE && + !strcmp(serd_node_string(hello2), "hello\"")); + check_copy_equals(hello2); + serd_node_free(hello2); + + SerdNode* hello_l = serd_new_literal("hello_l\"", NULL, "en"); + assert(serd_node_length(hello_l) == 8); + assert(!strcmp(serd_node_string(hello_l), "hello_l\"")); + assert(serd_node_flags(hello_l) == (SERD_HAS_QUOTE | SERD_HAS_LANGUAGE)); + + const SerdNode* const lang = serd_node_language(hello_l); + assert(lang); + assert(!strcmp(serd_node_string(lang), "en")); + check_copy_equals(hello_l); + serd_node_free(hello_l); + + SerdNode* hello_dt = + serd_new_literal("hello_dt\"", "http://example.org/Thing", NULL); + assert(serd_node_length(hello_dt) == 9); + assert(!strcmp(serd_node_string(hello_dt), "hello_dt\"")); + assert(serd_node_flags(hello_dt) == (SERD_HAS_QUOTE | SERD_HAS_DATATYPE)); + + const SerdNode* const datatype = serd_node_datatype(hello_dt); + assert(datatype); + assert(!strcmp(serd_node_string(datatype), "http://example.org/Thing")); + + check_copy_equals(hello_dt); + serd_node_free(hello_dt); +} + int main(void) { @@ -206,6 +251,7 @@ main(void) test_node_equals(); test_node_from_string(); test_node_from_substring(); + test_literal(); printf("Success\n"); return 0; diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index 5aa2b850..245f5328 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -91,16 +91,12 @@ test_statement_sink(void* handle, const SerdNode* graph, const SerdNode* subject, const SerdNode* predicate, - const SerdNode* object, - const SerdNode* object_datatype, - const SerdNode* object_lang) + const SerdNode* object) { (void)flags; (void)subject; (void)predicate; (void)object; - (void)object_datatype; - (void)object_lang; ReaderTest* rt = (ReaderTest*)handle; ++rt->n_statement; @@ -471,57 +467,33 @@ test_writer(const char* const path) SerdNode* o = serd_new_string(SERD_LITERAL, (char*)buf); // Write 3 invalid statements (should write nothing) - const SerdNode* junk[][5] = { - {s, o, o, NULL, NULL}, {o, p, o, NULL, NULL}, {s, o, p, NULL, NULL}}; - for (size_t i = 0; i < sizeof(junk) / (sizeof(SerdNode*) * 5); ++i) { - assert(serd_writer_write_statement(writer, - 0, - NULL, - junk[i][0], - junk[i][1], - junk[i][2], - junk[i][3], - junk[i][4])); + const SerdNode* junk[][3] = {{s, o, o}, {o, p, o}, {s, o, p}}; + for (size_t i = 0; i < sizeof(junk) / (sizeof(SerdNode*) * 3); ++i) { + assert(serd_writer_write_statement( + writer, 0, NULL, junk[i][0], junk[i][1], junk[i][2])); } - SerdNode* t = serd_new_string(SERD_URI, "urn:Type"); - SerdNode* l = serd_new_string(SERD_LITERAL, "en"); - const SerdNode* good[][5] = {{s, p, o, NULL, NULL}, - {s, p, o, NULL, NULL}, - {s, p, o, t, NULL}, - {s, p, o, NULL, l}, - {s, p, o, t, l}, - {s, p, o, t, NULL}, - {s, p, o, NULL, l}, - {s, p, o, NULL, NULL}, - {s, p, o, NULL, NULL}, - {s, p, o, NULL, NULL}}; - for (size_t i = 0; i < sizeof(good) / (sizeof(SerdNode*) * 5); ++i) { - assert(!serd_writer_write_statement(writer, - 0, - NULL, - good[i][0], - good[i][1], - good[i][2], - good[i][3], - good[i][4])); + SerdNode* t = serd_new_literal((char*)buf, "urn:Type", NULL); + SerdNode* l = serd_new_literal((char*)buf, NULL, "en"); + const SerdNode* good[][3] = {{s, p, o}, {s, p, t}, {s, p, l}}; + for (size_t i = 0; i < sizeof(good) / (sizeof(SerdNode*) * 3); ++i) { + assert(!serd_writer_write_statement( + writer, 0, NULL, good[i][0], good[i][1], good[i][2])); } // Write statements with bad UTF-8 (should be replaced) const char bad_str[] = {(char)0xFF, (char)0x90, 'h', 'i', 0}; SerdNode* bad_lit = serd_new_string(SERD_LITERAL, bad_str); SerdNode* bad_uri = serd_new_string(SERD_URI, bad_str); - assert( - !serd_writer_write_statement(writer, 0, NULL, s, p, bad_lit, NULL, NULL)); - assert( - !serd_writer_write_statement(writer, 0, NULL, s, p, bad_uri, NULL, NULL)); + assert(!serd_writer_write_statement(writer, 0, NULL, s, p, bad_lit)); + assert(!serd_writer_write_statement(writer, 0, NULL, s, p, bad_uri)); serd_node_free(bad_lit); serd_node_free(bad_uri); // Write 1 valid statement serd_node_free(o); o = serd_new_string(SERD_LITERAL, "hello"); - assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o, NULL, NULL)); + assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o)); serd_writer_free(writer); serd_node_free(lit); @@ -591,7 +563,7 @@ test_reader(const char* path) assert(!st); assert(rt->n_base == 0); assert(rt->n_prefix == 0); - assert(rt->n_statement == 13); + assert(rt->n_statement == 6); assert(rt->n_end == 0); assert(rt->graph && serd_node_string(rt->graph) && !strcmp(serd_node_string(rt->graph), "http://example.org/")); diff --git a/test/test_writer.c b/test/test_writer.c index 9d004076..68d9e14e 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -31,7 +31,7 @@ test_write_long_literal(void) SerdNode* p = serd_new_string(SERD_URI, "http://example.org/p"); SerdNode* o = serd_new_string(SERD_LITERAL, "hello \"\"\"world\"\"\"!"); - assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o, NULL, NULL)); + assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o)); serd_node_free(o); serd_node_free(p); @@ -70,9 +70,7 @@ test_writer_cleanup(void) SerdNode* p = serd_new_string(SERD_URI, "http://example.org/p"); SerdNode* o = serd_new_string(SERD_BLANK, "http://example.org/o"); - st = serd_writer_write_statement( - writer, SERD_ANON_O_BEGIN, NULL, s, p, o, NULL, NULL); - + st = serd_writer_write_statement(writer, SERD_ANON_O_BEGIN, NULL, s, p, o); assert(!st); // Write the start of several nested anonymous objects @@ -83,7 +81,7 @@ test_writer_cleanup(void) SerdNode* next_o = serd_new_string(SERD_BLANK, buf); st = serd_writer_write_statement( - writer, SERD_ANON_O_BEGIN, NULL, o, p, next_o, NULL, NULL); + writer, SERD_ANON_O_BEGIN, NULL, o, p, next_o); serd_node_free(o); o = next_o; @@ -124,11 +122,11 @@ test_strict_write(void) SerdNode* bad_lit = serd_new_string(SERD_LITERAL, (const char*)bad_str); SerdNode* bad_uri = serd_new_string(SERD_URI, (const char*)bad_str); - assert(serd_writer_write_statement( - writer, 0, NULL, s, p, bad_lit, NULL, NULL) == SERD_BAD_TEXT); + assert(serd_writer_write_statement(writer, 0, NULL, s, p, bad_lit) == + SERD_BAD_TEXT); - assert(serd_writer_write_statement( - writer, 0, NULL, s, p, bad_uri, NULL, NULL) == SERD_BAD_TEXT); + assert(serd_writer_write_statement(writer, 0, NULL, s, p, bad_uri) == + SERD_BAD_TEXT); serd_node_free(bad_uri); serd_node_free(bad_lit); @@ -162,7 +160,7 @@ test_write_error(void) writer = serd_writer_new( SERD_TURTLE, (SerdWriterFlags)0, env, NULL, error_sink, NULL); assert(writer); - st = serd_writer_write_statement(writer, 0U, NULL, u, u, u, NULL, NULL); + st = serd_writer_write_statement(writer, 0U, NULL, u, u, u); assert(st == SERD_BAD_WRITE); serd_writer_free(writer); -- cgit v1.2.1