diff options
author | David Robillard <d@drobilla.net> | 2020-06-21 18:19:48 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-10-27 13:13:59 +0100 |
commit | 51f917f8a5179a0e16b32a7f478b4245ea7f2505 (patch) | |
tree | f09ce814c6a18ee572f035a3527ae5f2207403f9 | |
parent | 20e2f827282213714174e52ce7684e315545176c (diff) | |
download | serd-51f917f8a5179a0e16b32a7f478b4245ea7f2505.tar.gz serd-51f917f8a5179a0e16b32a7f478b4245ea7f2505.tar.bz2 serd-51f917f8a5179a0e16b32a7f478b4245ea7f2505.zip |
WIP: Add serd_node_from_syntax() and serd_node_to_syntax()
-rw-r--r-- | serd/serd.h | 38 | ||||
-rw-r--r-- | src/node_syntax.c | 97 | ||||
-rw-r--r-- | src/writer.c | 8 | ||||
-rw-r--r-- | src/writer.h | 21 | ||||
-rw-r--r-- | tests/node_syntax_test.c | 150 | ||||
-rw-r--r-- | tests/serd_test.c | 4 | ||||
-rw-r--r-- | wscript | 3 |
7 files changed, 319 insertions, 2 deletions
diff --git a/serd/serd.h b/serd/serd.h index bac74adc..a43acbca 100644 --- a/serd/serd.h +++ b/serd/serd.h @@ -671,6 +671,44 @@ serd_uri_serialise_relative(const SerdURI* uri, */ /** + Create a node from a string representation in `syntax`. + + The string should be a node as if written as an object in the given syntax, + without any extra quoting or punctuation, which is the format returned by + serd_node_to_syntax(). These two functions, when used with #SERD_TURTLE, + can be used to round-trip any node to a string and back. + + @param str String representation of a node. + + @param syntax Syntax to use. Should be either SERD_TURTLE or SERD_NTRIPLES + (the others are redundant). Note that namespaced (CURIE) nodes and relative + URIs can not be expressed in NTriples. + + @return A newly allocated node that must be freed with serd_node_free(). +*/ +SERD_API +SerdNode* +serd_node_from_syntax(const char* str, SerdSyntax syntax); + +/** + Return a string representation of `node` in `syntax`. + + The returned string represents that node as if written as an object in the + given syntax, without any extra quoting or punctuation. + + @param node Node to serialise. + + @param syntax Syntax to use. Should be either SERD_TURTLE or SERD_NTRIPLES + (the others are redundant). Note that namespaced (CURIE) nodes and relative + URIs can not be expressed in NTriples. + + @return A newly allocated string that must be freed with serd_free(). +*/ +SERD_API +char* +serd_node_to_syntax(const SerdNode* node, SerdSyntax syntax); + +/** Create a new "simple" node that is just a string. This can be used to create blank, CURIE, or URI nodes from an already diff --git a/src/node_syntax.c b/src/node_syntax.c new file mode 100644 index 00000000..10483188 --- /dev/null +++ b/src/node_syntax.c @@ -0,0 +1,97 @@ +/* + Copyright 2011-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "writer.h" + +#include "serd/serd.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static SerdStatus +on_node_string_event(void* handle, const SerdEvent* event) +{ + if (event->type == SERD_STATEMENT) { + *(SerdNode**)handle = serd_node_copy( + serd_statement_object(event->statement.statement)); + } + + return SERD_SUCCESS; +} + +SerdNode* +serd_node_from_syntax(const char* str, const SerdSyntax syntax) +{ + static const char* const prelude = + "_:s <http://www.w3.org/2000/01/rdf-schema#object>"; + + const size_t str_len = strlen(str); + const size_t doc_len = strlen(prelude) + str_len + 3; + char* const doc = (char*)calloc(doc_len + 1, 1); + + snprintf(doc, doc_len + 1, "%s %s .", prelude, str); + + SerdNode* object = NULL; + SerdWorld* world = serd_world_new(); + SerdSink* sink = serd_sink_new(&object, NULL); + + SerdByteSource* byte_source = serd_byte_source_new_string(doc, NULL); + + SerdReader* reader = serd_reader_new( + world, syntax, SERD_READ_EXACT_BLANKS, sink, 1024 + doc_len); + + serd_world_set_log_func(world, serd_quiet_error_func, NULL); + serd_sink_set_event_func(sink, on_node_string_event); + serd_reader_start(reader, byte_source); + serd_reader_read_document(reader); + serd_reader_finish(reader); + serd_byte_source_free(byte_source); + serd_reader_free(reader); + serd_sink_free(sink); + serd_world_free(world); + free(doc); + + return object; +} + +char* +serd_node_to_syntax(const SerdNode* node, const SerdSyntax syntax) +{ + SerdWorld* world = serd_world_new(); + SerdBuffer buffer = {NULL, 0}; + SerdByteSink* bsink = serd_byte_sink_new_buffer(&buffer); + SerdWriter* writer = serd_writer_new(world, syntax, 0, NULL, bsink); + + serd_world_set_log_func(world, serd_quiet_error_func, NULL); + + SerdStatus st = serd_writer_write_node(writer, node); + if (!st) { + st = serd_writer_finish(writer); + } + + serd_buffer_sink_finish(&buffer); + serd_writer_free(writer); + serd_byte_sink_free(bsink); + serd_world_free(world); + + if (st) { + serd_free(buffer.buf); + return NULL; + } + + return (char*)buffer.buf; +} diff --git a/src/writer.c b/src/writer.c index ed3be4e7..7acf7922 100644 --- a/src/writer.c +++ b/src/writer.c @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "writer.h" + #include "byte_sink.h" #include "env.h" #include "node.h" @@ -1049,6 +1051,12 @@ serd_writer_on_event(SerdWriter* writer, const SerdEvent* event) } SerdStatus +serd_writer_write_node(SerdWriter* writer, const SerdNode* node) +{ + return write_node(writer, node, SERD_OBJECT, 0); +} + +SerdStatus serd_writer_finish(SerdWriter* writer) { SerdStatus st = SERD_SUCCESS; diff --git a/src/writer.h b/src/writer.h new file mode 100644 index 00000000..e86fc775 --- /dev/null +++ b/src/writer.h @@ -0,0 +1,21 @@ +/* + Copyright 2019-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "serd/serd.h" + +SerdStatus +serd_writer_write_node(SerdWriter* writer, const SerdNode* node); + diff --git a/tests/node_syntax_test.c b/tests/node_syntax_test.c new file mode 100644 index 00000000..739a8b94 --- /dev/null +++ b/tests/node_syntax_test.c @@ -0,0 +1,150 @@ +/* + Copyright 2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#undef NDEBUG + +#include "serd/serd.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +static bool +test(const SerdSyntax syntax, SerdNode* const node, const char* const expected) +{ + char* const str = serd_node_to_syntax(node, syntax); + SerdNode* const copy = serd_node_from_syntax(str, syntax); + + const bool success = !strcmp(str, expected) && serd_node_equals(copy, node); + + serd_node_free(copy); + serd_free(str); + serd_node_free(node); + return success; +} + +static void +test_common(const SerdSyntax syntax) +{ + static const int data[] = {4, 2}; + + SerdNode* const datatype = serd_new_uri("http://example.org/Datatype"); + SerdNode* const num_type = serd_new_uri("http://example.org/Decimal"); + + assert(test(syntax, serd_new_string("node"), "\"node\"")); + + assert(test(syntax, serd_new_plain_literal("hallo", "de"), "\"hallo\"@de")); + + assert(test(syntax, + serd_new_typed_literal("X", datatype), + "\"X\"^^<http://example.org/Datatype>")); + + assert(test(syntax, serd_new_blank("blank"), "_:blank")); + assert(test(syntax, serd_new_blank("b0"), "_:b0")); + + assert(test(syntax, + serd_new_uri("http://example.org/"), + "<http://example.org/>")); + + assert(test(syntax, + serd_new_decimal(1.25, 4, 2, num_type), + "\"1.25\"^^<http://example.org/Decimal>")); + + assert(test(syntax, + serd_new_double(1.25), + "\"1.25E0\"^^<http://www.w3.org/2001/XMLSchema#double>")); + + assert(test(syntax, + serd_new_float(1.25), + "\"1.25E0\"^^<http://www.w3.org/2001/XMLSchema#float>")); + + assert(test(syntax, + serd_new_integer(1234, num_type), + "\"1234\"^^<http://example.org/Decimal>")); + + assert(test( + syntax, + serd_new_blob(data, sizeof(data), false, NULL), + "\"BAAAAAIAAAA=\"^^<http://www.w3.org/2001/XMLSchema#base64Binary>")); + + serd_node_free(num_type); + serd_node_free(datatype); +} + +static void +test_ntriples(void) +{ + SerdNode* const datatype = serd_new_uri("http://example.org/Datatype"); + SerdNode* const num_type = serd_new_uri("http://example.org/Decimal"); + + test_common(SERD_NTRIPLES); + + { + // No namespace prefixes in NTriples + SerdNode* const curie = serd_new_curie("cu:rie"); + assert(!serd_node_to_syntax(curie, SERD_NTRIPLES)); + serd_node_free(curie); + } + + { + // No relative URIs in NTriples + SerdNode* const uri = serd_new_uri("rel/uri"); + assert(!serd_node_to_syntax(uri, SERD_NTRIPLES)); + serd_node_free(uri); + } + + assert(test(SERD_NTRIPLES, + serd_new_decimal(1.25, 4, 2, NULL), + "\"1.25\"^^<http://www.w3.org/2001/XMLSchema#decimal>")); + + assert(test(SERD_NTRIPLES, + serd_new_integer(1234, NULL), + "\"1234\"^^<http://www.w3.org/2001/XMLSchema#integer>")); + + assert(test(SERD_NTRIPLES, + serd_new_boolean(true), + "\"true\"^^<http://www.w3.org/2001/XMLSchema#boolean>")); + + assert(test(SERD_NTRIPLES, + serd_new_boolean(false), + "\"false\"^^<http://www.w3.org/2001/XMLSchema#boolean>")); + + serd_node_free(num_type); + serd_node_free(datatype); +} + +static void +test_turtle(void) +{ + test_common(SERD_TURTLE); + + assert(test(SERD_TURTLE, serd_new_curie("cu:rie"), "cu:rie")); + assert(test(SERD_TURTLE, serd_new_uri("rel/uri"), "<rel/uri>")); + assert(test(SERD_TURTLE, serd_new_decimal(1.25, 4, 2, NULL), "1.25")); + assert(test(SERD_TURTLE, serd_new_integer(1234, NULL), "1234")); + assert(test(SERD_TURTLE, serd_new_boolean(true), "true")); + assert(test(SERD_TURTLE, serd_new_boolean(false), "false")); +} + +int +main(void) +{ + test_ntriples(); + test_turtle(); + + return 0; +} diff --git a/tests/serd_test.c b/tests/serd_test.c index 9f85136e..2acf24b0 100644 --- a/tests/serd_test.c +++ b/tests/serd_test.c @@ -375,7 +375,7 @@ test_node_equals(void) } static void -test_node_from_string(void) +test_node_from_syntax(void) { assert(!serd_new_string(NULL)); @@ -748,7 +748,7 @@ main(void) test_strerror(); test_uri_parsing(); test_node_equals(); - test_node_from_string(); + test_node_from_syntax(); test_node_from_substring(); test_simple_node(); test_literal(); @@ -177,6 +177,7 @@ lib_source = ['src/base64.c', 'src/model.c', 'src/n3.c', 'src/node.c', + 'src/node_syntax.c', 'src/nodes.c', 'src/normalise.c', 'src/range.c', @@ -265,6 +266,7 @@ def build(bld): ('free_null_test', 'tests/free_null_test.c'), ('int_math_test', 'tests/int_math_test.c'), ('model_test', 'tests/model_test.c'), + ('node_syntax_test', 'tests/node_syntax_test.c'), ('nodes_test', 'tests/nodes_test.c'), ('overflow_test', 'tests/overflow_test.c'), ('read_chunk_test', 'tests/read_chunk_test.c'), @@ -708,6 +710,7 @@ def test(tst): check(['./free_null_test']) check(['./int_math_test']) check(['./model_test']) + check(['./node_syntax_test']) check(['./nodes_test']) check(['./overflow_test']) check(['./read_chunk_test']) |