aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-08-12 22:33:47 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:07 -0500
commit196db2ef0cd44c3fb542b86be7929bd01d83e138 (patch)
tree97ad345394e394c0d42b43fcf3d464794f719ccb
parentfdf837b4b3baffc65e429c2e6ecc2e764bfed0ac (diff)
downloadserd-196db2ef0cd44c3fb542b86be7929bd01d83e138.tar.gz
serd-196db2ef0cd44c3fb542b86be7929bd01d83e138.tar.bz2
serd-196db2ef0cd44c3fb542b86be7929bd01d83e138.zip
Put rdf:type properties first when pretty-printing
This is a common convention in Turtle and TriG because the special "a" syntax for rdf type as the first property looks nice, makes things easier to read, and can be useful for streaming implementations because the type of the instance is known before reading its properties. Also significantly clean up the pretty-printing implementation in the process.
-rw-r--r--include/serd/serd.h14
-rw-r--r--src/cursor.c9
-rw-r--r--src/describe.c180
-rw-r--r--src/writer.c2
-rw-r--r--test/sort/GOPS.nq3
-rw-r--r--test/sort/GOSP.nq3
-rw-r--r--test/sort/GPSO.nq3
-rw-r--r--test/sort/GSOP.nq3
-rw-r--r--test/sort/GSPO.nq3
-rw-r--r--test/sort/OPS.nq3
-rw-r--r--test/sort/OSP.nq3
-rw-r--r--test/sort/POS.nq3
-rw-r--r--test/sort/PSO.nq3
-rw-r--r--test/sort/SOP.nq3
-rw-r--r--test/sort/SPO.nq3
-rw-r--r--test/sort/input.trig27
-rw-r--r--test/sort/pretty.nq3
-rw-r--r--test/sort/untyped.nq12
-rwxr-xr-xtest/test_sort.py16
-rw-r--r--tools/console.c1
-rw-r--r--tools/serd-sort.c8
21 files changed, 214 insertions, 91 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 1f46e092..07f6b9f5 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -2523,7 +2523,14 @@ typedef enum {
particular invalid UTF-8 text. Note that this flag should be used
carefully, since it can result in data loss.
*/
- SERD_WRITE_LAX = 1u << 4u
+ SERD_WRITE_LAX = 1u << 4u,
+
+ /**
+ Write rdf:type as a normal predicate.
+
+ This disables the special "a" syntax in Turtle and TriG.
+ */
+ SERD_WRITE_RDF_TYPE = 1u << 5u
} SerdWriterFlag;
/// Bitwise OR of SerdWriterFlag values
@@ -2629,7 +2636,7 @@ serd_cursor_copy(const SerdCursor* SERD_NULLABLE cursor);
/// Return the statement pointed to by `cursor`
SERD_API
const SerdStatement* SERD_NULLABLE
-serd_cursor_get(const SerdCursor* SERD_NONNULL cursor);
+serd_cursor_get(const SerdCursor* SERD_NULLABLE cursor);
/**
Increment cursor to point to the next statement.
@@ -2671,7 +2678,8 @@ serd_cursor_free(SerdCursor* SERD_NULLABLE cursor);
/// Flags that control the style of a model serialisation
typedef enum {
- SERD_NO_INLINE_OBJECTS = 1u << 0u ///< Disable object inlining
+ SERD_NO_INLINE_OBJECTS = 1u << 0u, ///< Disable object inlining
+ SERD_NO_TYPE_FIRST = 1u << 1u ///< Disable writing rdf:type ("a") first
} SerdDescribeFlag;
/// Bitwise OR of SerdDescribeFlag values
diff --git a/src/cursor.c b/src/cursor.c
index 4213476e..650ac771 100644
--- a/src/cursor.c
+++ b/src/cursor.c
@@ -146,11 +146,10 @@ serd_cursor_copy(const SerdCursor* const cursor)
const SerdStatement*
serd_cursor_get(const SerdCursor* const cursor)
{
- assert(cursor);
-
- return ((!zix_btree_iter_is_end(cursor->iter) && check_version(cursor))
- ? (const SerdStatement*)zix_btree_get(cursor->iter)
- : NULL);
+ return (
+ (cursor && !zix_btree_iter_is_end(cursor->iter) && check_version(cursor))
+ ? (const SerdStatement*)zix_btree_get(cursor->iter)
+ : NULL);
}
SerdStatus
diff --git a/src/describe.c b/src/describe.c
index 718eaf1c..9328f261 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -29,17 +29,24 @@
#include <assert.h>
#include <stdbool.h>
-#include <stdlib.h>
+#include <stddef.h>
typedef enum { NAMED, ANON_S, ANON_O, LIST_S, LIST_O } NodeStyle;
+typedef struct {
+ const SerdModel* model; // Model to read from
+ const SerdSink* sink; // Sink to write description to
+ ZixHash* list_subjects; // Nodes written in the current list or null
+ SerdDescribeFlags flags; // Flags to control description
+} DescribeContext;
+
static SerdStatus
-write_range_statement(const SerdSink* sink,
- const SerdModel* model,
- ZixHash* list_subjects,
- unsigned depth,
- SerdStatementFlags flags,
- const SerdStatement* statement);
+write_range_statement(const DescribeContext* ctx,
+ unsigned depth,
+ SerdStatementFlags statement_flags,
+ const SerdStatement* statement,
+ const SerdNode* last_subject,
+ bool write_types);
static NodeStyle
get_node_style(const SerdModel* const model, const SerdNode* const node)
@@ -81,39 +88,41 @@ ptr_equals(const SerdNode* const a, const SerdNode* const b)
}
static SerdStatus
-write_pretty_range(const SerdSink* const sink,
- const unsigned depth,
- const SerdModel* const model,
- SerdCursor* const range)
+write_pretty_range(const DescribeContext* const ctx,
+ const unsigned depth,
+ SerdCursor* const range,
+ const SerdNode* last_subject,
+ bool write_types)
{
- ZixHash* const list_subjects = zix_hash_new(identity, ptr_hash, ptr_equals);
- SerdStatus st = SERD_SUCCESS;
-
- while (!st && !serd_cursor_is_end(range)) {
- const SerdStatement* const statement = serd_cursor_get(range);
- assert(statement);
+ SerdStatus st = SERD_SUCCESS;
+ const SerdStatement* statement = serd_cursor_get(range);
- if (!(st = write_range_statement(
- sink, model, list_subjects, depth, 0, statement))) {
- st = serd_cursor_advance(range);
+ while (statement) {
+ // Write this statement (and possibly more to describe anonymous nodes)
+ if ((st = write_range_statement(
+ ctx, depth, 0u, statement, last_subject, write_types))) {
+ break;
}
- }
- zix_hash_free(list_subjects);
+ // Update the last subject and advance the cursor
+ last_subject = serd_statement_subject(statement);
+ st = serd_cursor_advance(range);
+ statement = serd_cursor_get(range);
+ }
return st > SERD_FAILURE ? st : SERD_SUCCESS;
}
static SerdStatus
-write_list(const SerdSink* const sink,
- const SerdModel* const model,
- ZixHash* const list_subjects,
- const unsigned depth,
- SerdStatementFlags flags,
- const SerdNode* object,
- const SerdNode* const graph)
+write_list(const DescribeContext* const ctx,
+ const unsigned depth,
+ SerdStatementFlags flags,
+ const SerdNode* object,
+ const SerdNode* const graph)
{
+ const SerdModel* const model = ctx->model;
const SerdWorld* const world = model->world;
+ const SerdSink* const sink = ctx->sink;
const SerdNode* const rdf_first = world->rdf_first;
const SerdNode* const rdf_rest = world->rdf_rest;
const SerdNode* const rdf_nil = world->rdf_nil;
@@ -126,8 +135,7 @@ write_list(const SerdSink* const sink,
while (!st && !serd_node_equals(object, rdf_nil)) {
// Write rdf:first statement for this node
- if ((st = write_range_statement(
- sink, model, list_subjects, depth, flags, fs))) {
+ if ((st = write_range_statement(ctx, depth, flags, fs, NULL, false))) {
return st;
}
@@ -177,24 +185,47 @@ skip_range_statement(const SerdModel* const model,
}
static SerdStatus
-write_range_statement(const SerdSink* const sink,
- const SerdModel* const model,
- ZixHash* const list_subjects,
- const unsigned depth,
- SerdStatementFlags flags,
- const SerdStatement* SERD_NONNULL statement)
+write_subject_types(const DescribeContext* const ctx,
+ const unsigned depth,
+ const SerdNode* const subject,
+ const SerdNode* const graph)
{
- const SerdNode* const subject = serd_statement_subject(statement);
- const NodeStyle subject_style = get_node_style(model, subject);
- const SerdNode* const object = serd_statement_object(statement);
- const NodeStyle object_style = get_node_style(model, object);
- const SerdNode* const graph = serd_statement_graph(statement);
- SerdStatus st = SERD_SUCCESS;
+ SerdStatus st = SERD_SUCCESS;
+ SerdCursor* const t = serd_model_find(
+ ctx->model, subject, ctx->model->world->rdf_type, NULL, graph);
- if (subject_style == ANON_S) { // Write anonymous subject like "[] p o"
- flags |= SERD_EMPTY_S;
+ if (t) {
+ st = write_pretty_range(ctx, depth + 1, t, subject, true);
}
+ serd_cursor_free(t);
+ return st;
+}
+
+static bool
+types_first_for_subject(const DescribeContext* const ctx, const NodeStyle style)
+{
+ return style != LIST_S && !(ctx->flags & SERD_NO_TYPE_FIRST);
+}
+
+static SerdStatus
+write_range_statement(const DescribeContext* const ctx,
+ const unsigned depth,
+ SerdStatementFlags statement_flags,
+ const SerdStatement* SERD_NONNULL statement,
+ const SerdNode* SERD_NULLABLE last_subject,
+ const bool write_types)
+{
+ const SerdModel* const model = ctx->model;
+ const SerdSink* const sink = ctx->sink;
+ const SerdNode* const subject = serd_statement_subject(statement);
+ const NodeStyle subject_style = get_node_style(model, subject);
+ const SerdNode* const predicate = serd_statement_predicate(statement);
+ const SerdNode* const object = serd_statement_object(statement);
+ const NodeStyle object_style = get_node_style(model, object);
+ const SerdNode* const graph = serd_statement_graph(statement);
+ SerdStatus st = SERD_SUCCESS;
+
if (depth == 0u) {
if (skip_range_statement(model, statement)) {
return SERD_SUCCESS; // Skip subject that will be inlined elsewhere
@@ -202,9 +233,8 @@ write_range_statement(const SerdSink* const sink,
if (subject_style == LIST_S) {
// First write inline list subject, which this statement will follow
- if (zix_hash_insert(list_subjects, subject) != ZIX_STATUS_EXISTS) {
- st = write_list(
- sink, model, list_subjects, 2, flags | SERD_LIST_S, subject, graph);
+ if (zix_hash_insert(ctx->list_subjects, subject) != ZIX_STATUS_EXISTS) {
+ st = write_list(ctx, 2, statement_flags | SERD_LIST_S, subject, graph);
}
}
}
@@ -213,29 +243,42 @@ write_range_statement(const SerdSink* const sink,
return st;
}
- if (object_style == ANON_O) { // Write anonymous object like "[ ... ]"
- SerdCursor* const iter = serd_model_find(model, object, NULL, NULL, NULL);
+ // If this is a new subject, write types first if necessary
+ const bool types_first = types_first_for_subject(ctx, subject_style);
+ if (subject != last_subject && types_first) {
+ st = write_subject_types(ctx, depth, subject, graph);
+ }
- flags |= SERD_ANON_O;
- if (!(st = serd_sink_write_statement(sink, flags, statement))) {
- if (!(st = write_pretty_range(sink, depth + 1, model, iter))) {
- st = serd_sink_write_end(sink, object);
- }
- }
+ // Skip type statement if it would be written another time (just above)
+ if (subject_style != LIST_S && !write_types &&
+ serd_node_equals(predicate, model->world->rdf_type)) {
+ return st;
+ }
- serd_cursor_free(iter);
+ // Set up the flags for this statement
+ statement_flags |=
+ (((subject_style == ANON_S) * (SerdStatementFlags)SERD_EMPTY_S) |
+ ((object_style == ANON_O) * (SerdStatementFlags)SERD_ANON_O) |
+ ((object_style == LIST_O) * (SerdStatementFlags)SERD_LIST_O));
- } else if (object_style == LIST_O) { // Write list object like "( ... )"
- if (!(st =
- serd_sink_write_statement(sink, flags | SERD_LIST_O, statement))) {
- flags = flags & ~((unsigned)SERD_LIST_S);
+ // Finally write this statement
+ if ((st = serd_sink_write_statement(sink, statement_flags, statement))) {
+ return st;
+ }
- st =
- write_list(sink, model, list_subjects, depth + 1, flags, object, graph);
+ if (object_style == ANON_O) {
+ // Follow an anonymous object with its description like "[ ... ]"
+ SerdCursor* const iter = serd_model_find(model, object, NULL, NULL, NULL);
+
+ if (!(st = write_pretty_range(ctx, depth + 1, iter, last_subject, false))) {
+ st = serd_sink_write_end(sink, object);
}
- } else {
- st = serd_sink_write_statement(sink, flags, statement);
+ serd_cursor_free(iter);
+
+ } else if (object_style == LIST_O) {
+ // Follow a list object with its description like "( ... )"
+ st = write_list(ctx, depth + 1, 0u, object, graph);
}
return st;
@@ -264,7 +307,12 @@ serd_describe_range(const SerdCursor* const range,
}
}
} else {
- st = write_pretty_range(sink, 0, range->model, &copy);
+ DescribeContext ctx = {
+ range->model, sink, zix_hash_new(identity, ptr_hash, ptr_equals), flags};
+
+ st = write_pretty_range(&ctx, 0, &copy, NULL, (flags & SERD_NO_TYPE_FIRST));
+
+ zix_hash_free(ctx.list_subjects);
}
return st;
diff --git a/src/writer.c b/src/writer.c
index 30e09a4f..b7814158 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -841,7 +841,7 @@ write_uri_node(SerdWriter* const writer,
const char* node_str = serd_node_string(node);
const bool has_scheme = serd_uri_string_has_scheme(node_str);
if (supports_abbrev(writer)) {
- if (field == SERD_PREDICATE &&
+ if (!(writer->flags & SERD_WRITE_RDF_TYPE) && field == SERD_PREDICATE &&
serd_node_equals(node, writer->world->rdf_type)) {
return esink("a", 1, writer);
}
diff --git a/test/sort/GOPS.nq b/test/sort/GOPS.nq
index c7472e03..3d033b6e 100644
--- a/test/sort/GOPS.nq
+++ b/test/sort/GOPS.nq
@@ -1,10 +1,13 @@
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
diff --git a/test/sort/GOSP.nq b/test/sort/GOSP.nq
index c7472e03..3d033b6e 100644
--- a/test/sort/GOSP.nq
+++ b/test/sort/GOSP.nq
@@ -1,10 +1,13 @@
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
diff --git a/test/sort/GPSO.nq b/test/sort/GPSO.nq
index 1a858017..67e13aa7 100644
--- a/test/sort/GPSO.nq
+++ b/test/sort/GPSO.nq
@@ -7,4 +7,7 @@ _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
diff --git a/test/sort/GSOP.nq b/test/sort/GSOP.nq
index fc073a00..dbfcb7c1 100644
--- a/test/sort/GSOP.nq
+++ b/test/sort/GSOP.nq
@@ -1,10 +1,13 @@
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
diff --git a/test/sort/GSPO.nq b/test/sort/GSPO.nq
index 726b1d42..5aab6e1e 100644
--- a/test/sort/GSPO.nq
+++ b/test/sort/GSPO.nq
@@ -1,10 +1,13 @@
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
diff --git a/test/sort/OPS.nq b/test/sort/OPS.nq
index 456ade7f..593760be 100644
--- a/test/sort/OPS.nq
+++ b/test/sort/OPS.nq
@@ -1,6 +1,9 @@
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
diff --git a/test/sort/OSP.nq b/test/sort/OSP.nq
index 456ade7f..593760be 100644
--- a/test/sort/OSP.nq
+++ b/test/sort/OSP.nq
@@ -1,6 +1,9 @@
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
diff --git a/test/sort/POS.nq b/test/sort/POS.nq
index 51c675de..20eabfe1 100644
--- a/test/sort/POS.nq
+++ b/test/sort/POS.nq
@@ -8,3 +8,6 @@ _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
diff --git a/test/sort/PSO.nq b/test/sort/PSO.nq
index 0fb7bd68..113dd549 100644
--- a/test/sort/PSO.nq
+++ b/test/sort/PSO.nq
@@ -8,3 +8,6 @@ _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
diff --git a/test/sort/SOP.nq b/test/sort/SOP.nq
index 1692689c..7867eb24 100644
--- a/test/sort/SOP.nq
+++ b/test/sort/SOP.nq
@@ -1,7 +1,10 @@
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
diff --git a/test/sort/SPO.nq b/test/sort/SPO.nq
index 508debc7..2b09a976 100644
--- a/test/sort/SPO.nq
+++ b/test/sort/SPO.nq
@@ -1,9 +1,12 @@
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
diff --git a/test/sort/input.trig b/test/sort/input.trig
index 154a9fb8..9561d9b8 100644
--- a/test/sort/input.trig
+++ b/test/sort/input.trig
@@ -1,19 +1,22 @@
@prefix eg: <http://example.org/> .
eg:graph1 {
-eg:s
- eg:blank [
- eg:with eg:aProperty ,
- eg:orAnother
- ] ;
- eg:list (
- 1
- 2
- ) ;
- eg:literal "s1" .
+ eg:s
+ a eg:Subject ;
+ eg:blank [
+ a eg:Anonymous ;
+ eg:with eg:aProperty ,
+ eg:orAnother
+ ] ;
+ eg:list (
+ 1
+ 2
+ ) ;
+ eg:literal "s1" .
}
eg:graph2 {
-eg:a
- eg:b eg:c .
+ eg:a
+ a eg:OtherSubject ;
+ eg:b eg:c .
}
diff --git a/test/sort/pretty.nq b/test/sort/pretty.nq
index 451247d4..97851b82 100644
--- a/test/sort/pretty.nq
+++ b/test/sort/pretty.nq
@@ -1,4 +1,6 @@
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
+_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Anonymous> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
@@ -7,4 +9,5 @@ _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
diff --git a/test/sort/untyped.nq b/test/sort/untyped.nq
new file mode 100644
index 00000000..74625d7d
--- /dev/null
+++ b/test/sort/untyped.nq
@@ -0,0 +1,12 @@
+<http://example.org/s> <http://example.org/blank> _:b1 <http://example.org/graph1> .
+_:b1 <http://example.org/with> <http://example.org/aProperty> <http://example.org/graph1> .
+_:b1 <http://example.org/with> <http://example.org/orAnother> <http://example.org/graph1> .
+<http://example.org/s> <http://example.org/list> _:b2 <http://example.org/graph1> .
+_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
+_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 <http://example.org/graph1> .
+_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> <http://example.org/graph1> .
+_:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.org/graph1> .
+<http://example.org/s> <http://example.org/literal> "s1" <http://example.org/graph1> .
+<http://example.org/s> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Subject> <http://example.org/graph1> .
+<http://example.org/a> <http://example.org/b> <http://example.org/c> <http://example.org/graph2> .
+<http://example.org/a> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/OtherSubject> <http://example.org/graph2> .
diff --git a/test/test_sort.py b/test/test_sort.py
index 4080b93c..04c8ba10 100755
--- a/test/test_sort.py
+++ b/test/test_sort.py
@@ -28,15 +28,17 @@ collations = [
]
-def check(test_dir, command_prefix, out_dir, input_path, name):
+def check(test_dir, command_prefix, out_dir, input_path, name, flags=None):
"""Sort a single input in the named order and check the output.
- The expected output is assumed to exist at test_dir/NAME.nq.
+ The expected output is assumed to exist at test_dir/NAME.untyped.nq.
"""
output_path = os.path.join(out_dir, name + ".nq")
result_path = os.path.join(test_dir, name + ".nq")
- options = [] if name == "pretty" else ["-c", name]
+ options = flags if flags is not None else []
+ if name not in ["pretty", "untyped"]:
+ options += ["-c", name]
# Randomly add irrelevant options just to cover them
if random.choice([True, False]):
@@ -68,10 +70,18 @@ def run_tests(test_dir, command_prefix, out_dir):
input_trig = os.path.join(test_dir, "input.trig")
n_failures = 0
+
+ # Test all the basic collations, and "pretty" with type first
for name in collations:
if not check(test_dir, command_prefix, out_dir, input_trig, name):
n_failures += 1
+ # Test "pretty" without type first
+ if not check(
+ test_dir, command_prefix, out_dir, input_trig, "untyped", ["-t"]
+ ):
+ n_failures += 1
+
return n_failures
diff --git a/tools/console.c b/tools/console.c
index f1e78d75..d7f67c35 100644
--- a/tools/console.c
+++ b/tools/console.c
@@ -218,6 +218,7 @@ serd_set_output_option(const SerdStringView name,
{"verbatim", SERD_WRITE_VERBATIM},
{"terse", SERD_WRITE_TERSE},
{"lax", SERD_WRITE_LAX},
+ {"rdf_type", SERD_WRITE_RDF_TYPE},
{NULL, SERD_WRITE_ASCII},
};
diff --git a/tools/serd-sort.c b/tools/serd-sort.c
index 8ba99445..5e71e95e 100644
--- a/tools/serd-sort.c
+++ b/tools/serd-sort.c
@@ -176,7 +176,8 @@ print_usage(const char* const name, const bool error)
" -c COLLATION An optional \"G\" then the letters \"SPO\" in any order.\n"
" -h Display this help and exit.\n"
" -k BYTES Parser stack size.\n"
- " -o FILENAME Write output to FILENAME instead of stdout.\n";
+ " -o FILENAME Write output to FILENAME instead of stdout.\n"
+ " -t Do not write type as \"a\" before other properties.\n";
FILE* const os = error ? stderr : stdout;
fprintf(os, "%s", error ? "\n" : "");
@@ -221,6 +222,11 @@ parse_option(OptionIter* const iter, Options* const opts)
case 's':
return serd_get_argument(iter, &opts->input_string);
+ case 't':
+ opts->common.output.flags |= SERD_WRITE_RDF_TYPE;
+ opts->flags |= SERD_NO_TYPE_FIRST;
+ return serd_option_iter_advance(iter);
+
default:
break;
}