aboutsummaryrefslogtreecommitdiffstats
path: root/src/writer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/writer.c')
-rw-r--r--src/writer.c201
1 files changed, 157 insertions, 44 deletions
diff --git a/src/writer.c b/src/writer.c
index 469e0090..84bd465b 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -36,6 +36,43 @@ static const WriteContext WRITE_CONTEXT_NULL = {
{ 0, 0, 0, 0, SERD_NOTHING }
};
+typedef enum {
+ SEP_NONE,
+ SEP_END_S, ///< End of a subject ('.')
+ SEP_END_P, ///< End of a predicate (';')
+ SEP_END_O, ///< End of an object (',')
+ SEP_S_P, ///< Between a subject and predicate (whitespace)
+ SEP_P_O, ///< Between a predicate and object (whitespace)
+ SEP_ANON_BEGIN, ///< Start of anonymous node ('[')
+ SEP_ANON_END, ///< End of anonymous node (']')
+ SEP_LIST_BEGIN, ///< Start of list ('(')
+ SEP_LIST_SEP, ///< List separator (whitespace)
+ SEP_LIST_END ///< End of list (')')
+} Sep;
+
+typedef struct {
+ const char* str; ///< Sep string
+ uint8_t len; ///< Length of sep string
+ uint8_t space_before; ///< Newline before sep
+ uint8_t space_after_node; ///< Newline after sep if after node
+ uint8_t space_after_sep; ///< Newline after sep if after sep
+} SepRule;
+
+static const SepRule rules[] = {
+ { NULL, 0, 0, 0, 0 },
+ { " .\n\n", 4, 0, 0, 0 },
+ { " ;", 2, 0, 1, 1 },
+ { " ,", 2, 0, 1, 0 },
+ { NULL, 0, 0, 1, 0 },
+ { " ", 1, 0, 0, 0 },
+ { "[", 1, 0, 1, 1 },
+ { "]", 1, 0, 0, 0 },
+ { "(", 1, 0, 0, 0 },
+ { NULL, 1, 0, 1, 0 },
+ { ")", 1, 1, 0, 0 },
+ { "\n", 1, 0, 1, 0 }
+};
+
struct SerdWriterImpl {
SerdSyntax syntax;
SerdStyle style;
@@ -46,9 +83,12 @@ struct SerdWriterImpl {
SerdSink sink;
void* stream;
WriteContext context;
+ SerdNode list_subj;
+ unsigned list_depth;
uint8_t* bprefix;
size_t bprefix_len;
unsigned indent;
+ Sep last_sep;
bool empty;
};
@@ -202,16 +242,8 @@ write_text(SerdWriter* writer, TextContext ctx,
}
static void
-serd_writer_write_delim(SerdWriter* writer, const uint8_t delim)
+write_newline(SerdWriter* writer)
{
- switch (delim) {
- case '\n':
- break;
- default:
- sink(" ", 1, writer);
- case '[':
- sink(&delim, 1, writer);
- }
sink("\n", 1, writer);
for (unsigned i = 0; i < writer->indent; ++i) {
sink("\t", 1, writer);
@@ -219,6 +251,25 @@ serd_writer_write_delim(SerdWriter* writer, const uint8_t delim)
}
static void
+write_sep(SerdWriter* writer, const Sep sep)
+{
+ const SepRule* rule = &rules[sep];
+ if (rule->space_before) {
+ write_newline(writer);
+ }
+ if (rule->str) {
+ sink(rule->str, rule->len, writer);
+ }
+ if ( (writer->last_sep && rule->space_after_sep)
+ || (!writer->last_sep && rule->space_after_node)) {
+ write_newline(writer);
+ } else if (writer->last_sep && rule->space_after_node) {
+ sink(" ", 1, writer);
+ }
+ writer->last_sep = sep;
+}
+
+static void
reset_context(SerdWriter* writer, bool del)
{
if (del) {
@@ -257,10 +308,22 @@ write_node(SerdWriter* writer,
&& ((field == FIELD_SUBJECT && (flags & SERD_ANON_S_BEGIN))
|| (field == FIELD_OBJECT && (flags & SERD_ANON_O_BEGIN)))) {
++writer->indent;
- serd_writer_write_delim(writer, '[');
+ write_sep(writer, SEP_ANON_BEGIN);
+ } else if (writer->syntax != SERD_NTRIPLES
+ && (field == FIELD_SUBJECT && (flags & SERD_LIST_S_BEGIN))) {
+ assert(writer->list_depth == 0);
+ copy_node(&writer->list_subj, node);
+ ++writer->list_depth;
+ ++writer->indent;
+ write_sep(writer, SEP_LIST_BEGIN);
+ } else if (writer->syntax != SERD_NTRIPLES
+ && (field == FIELD_OBJECT && (flags & SERD_LIST_O_BEGIN))) {
+ ++writer->indent;
+ ++writer->list_depth;
+ write_sep(writer, SEP_LIST_BEGIN);
} else if (writer->syntax != SERD_NTRIPLES
- && ((field == FIELD_SUBJECT && (flags & SERD_EMPTY_S))
- || (field == FIELD_OBJECT && (flags & SERD_EMPTY_O)))) {
+ && ((field == FIELD_SUBJECT && (flags & SERD_EMPTY_S))
+ || (field == FIELD_OBJECT && (flags & SERD_EMPTY_O)))) {
sink("[]", 2, writer);
} else {
sink("_:", 2, writer);
@@ -325,7 +388,11 @@ write_node(SerdWriter* writer,
if ((writer->syntax == SERD_TURTLE)
&& !strcmp((const char*)node->buf, NS_RDF "type")) {
sink("a", 1, writer);
- return true;
+ break;
+ } else if ((writer->syntax == SERD_TURTLE)
+ && !strcmp((const char*)node->buf, NS_RDF "nil")) {
+ sink("()", 2, writer);
+ break;
} else if ((writer->style & SERD_STYLE_CURIED)
&& serd_uri_string_has_scheme(node->buf)) {
SerdNode prefix;
@@ -334,7 +401,7 @@ write_node(SerdWriter* writer,
write_text(writer, WRITE_URI, prefix.buf, prefix.n_bytes, '>');
sink(":", 1, writer);
write_text(writer, WRITE_URI, suffix.buf, suffix.len, '>');
- return true;
+ break;
}
} else if ((writer->style & SERD_STYLE_RESOLVED)
&& !serd_uri_string_has_scheme(node->buf)) {
@@ -345,7 +412,7 @@ write_node(SerdWriter* writer,
sink("<", 1, writer);
serd_uri_serialise(&abs_uri, (SerdSink)sink, writer);
sink(">", 1, writer);
- return true;
+ break;
}
sink("<", 1, writer);
write_text(writer, WRITE_URI, node->buf, node->n_bytes, '>');
@@ -353,6 +420,7 @@ write_node(SerdWriter* writer,
default:
break;
}
+ writer->last_sep = SEP_NONE;
return true;
}
@@ -367,6 +435,45 @@ is_resource(const SerdNode* node)
}
}
+static void
+serd_writer_write_predicate(SerdWriter* writer,
+ SerdStatementFlags flags,
+ const SerdNode* predicate)
+{
+ if (!(flags & (SERD_LIST_CONT|SERD_LIST_S_BEGIN))) {
+ write_node(writer, predicate, NULL, NULL, FIELD_PREDICATE, flags);
+ write_sep(writer, SEP_P_O);
+ }
+ copy_node(&writer->context.predicate, predicate);
+}
+
+static bool
+serd_writer_write_object(SerdWriter* writer,
+ SerdStatementFlags flags,
+ const SerdNode* predicate,
+ const SerdNode* object,
+ const SerdNode* object_datatype,
+ const SerdNode* object_lang)
+{
+ if (!strcmp((const char*)object->buf, NS_RDF "nil")) {
+ if (flags & SERD_LIST_CONT) {
+ --writer->indent;
+ write_sep(writer, SEP_LIST_END);
+ return true;
+ } else {
+ sink("()", 2, writer);
+ }
+ } else if (strcmp((const char*)predicate->buf, NS_RDF "rest")) {
+ if (!strcmp((const char*)predicate->buf, NS_RDF "first")) {
+ write_sep(writer, SEP_LIST_SEP);
+ }
+
+ write_node(writer, object, object_datatype, object_lang,
+ FIELD_OBJECT, flags);
+ }
+ return false;
+}
+
SERD_API
SerdStatus
serd_writer_write_statement(SerdWriter* writer,
@@ -399,15 +506,15 @@ serd_writer_write_statement(SerdWriter* writer,
default:
break;
}
+
+ bool was_list_end = false;
if (serd_node_equals(subject, &writer->context.subject)) {
if (serd_node_equals(predicate, &writer->context.predicate)) {
// Abbreviate S P
- if ((flags & SERD_ANON_O_BEGIN)) {
- sink(" , ", 3, writer); // ] , [
- } else {
+ if (!(flags & SERD_ANON_O_BEGIN)) {
++writer->indent;
- serd_writer_write_delim(writer, ',');
}
+ write_sep(writer, SEP_END_O);
write_node(writer, object, object_datatype, object_lang,
FIELD_OBJECT, flags);
if (!(flags & SERD_ANON_O_BEGIN)) {
@@ -415,35 +522,34 @@ serd_writer_write_statement(SerdWriter* writer,
}
} else {
// Abbreviate S
- if (writer->context.predicate.type) {
- serd_writer_write_delim(writer, ';');
- } else {
- serd_writer_write_delim(writer, '\n');
+ if (!(flags & SERD_LIST_CONT)) {
+ write_sep(writer,
+ (writer->context.predicate.type
+ ? SEP_END_P : SEP_S_P));
}
- write_node(writer, predicate, NULL, NULL, FIELD_PREDICATE, flags);
- copy_node(&writer->context.predicate, predicate);
- sink(" ", 1, writer);
- write_node(writer, object, object_datatype, object_lang,
- FIELD_OBJECT, flags);
+
+ serd_writer_write_predicate(writer, flags, predicate);
+
+ was_list_end = serd_writer_write_object(
+ writer, flags, predicate, object, object_datatype, object_lang);
}
} else {
+ // No abbreviation
if (writer->context.subject.type) {
assert(writer->indent > 0);
--writer->indent;
- if (serd_stack_is_empty(&writer->anon_stack)) {
- serd_writer_write_delim(writer, '.');
- serd_writer_write_delim(writer, '\n');
+ if (serd_stack_is_empty(&writer->anon_stack)
+ && !(flags & SERD_LIST_CONT)) {
+ write_sep(writer, SEP_END_S);
}
} else if (!writer->empty) {
- serd_writer_write_delim(writer, '\n');
+ write_sep(writer, SEP_S_P);
}
- if (!(flags & SERD_ANON_CONT)) {
+ if (!(flags & (SERD_ANON_CONT|SERD_LIST_CONT))) {
write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags);
++writer->indent;
- if (!(flags & SERD_ANON_S_BEGIN)) {
- serd_writer_write_delim(writer, '\n');
- }
+ write_sep(writer, SEP_S_P);
} else {
++writer->indent;
}
@@ -451,15 +557,19 @@ serd_writer_write_statement(SerdWriter* writer,
reset_context(writer, false);
copy_node(&writer->context.subject, subject);
- write_node(writer, predicate, NULL, NULL, FIELD_PREDICATE, flags);
- copy_node(&writer->context.predicate, predicate);
- sink(" ", 1, writer);
+ serd_writer_write_predicate(writer, flags, predicate);
- write_node(writer, object, object_datatype, object_lang,
- FIELD_OBJECT, flags);
+ was_list_end = serd_writer_write_object(
+ writer, flags, predicate, object, object_datatype, object_lang);
}
- if ((flags & SERD_ANON_S_BEGIN) || (flags & SERD_ANON_O_BEGIN)) {
+ if (was_list_end) {
+ if (--writer->list_depth == 0 && writer->list_subj.type) {
+ reset_context(writer, false);
+ writer->context.subject = writer->list_subj;
+ writer->list_subj = SERD_NODE_NULL;
+ }
+ } else if (flags & (SERD_ANON_S_BEGIN|SERD_ANON_O_BEGIN)) {
WriteContext* ctx = (WriteContext*)serd_stack_push(
&writer->anon_stack, sizeof(WriteContext));
*ctx = writer->context;
@@ -490,8 +600,8 @@ serd_writer_end_anon(SerdWriter* writer,
}
assert(writer->indent > 0);
--writer->indent;
- serd_writer_write_delim(writer, '\n');
- sink("]", 1, writer);
+ write_sep(writer, SEP_END_P);
+ write_sep(writer, SEP_ANON_END);
reset_context(writer, true);
writer->context = *anon_stack_top(writer);
serd_stack_pop(&writer->anon_stack, sizeof(WriteContext));
@@ -536,9 +646,12 @@ serd_writer_new(SerdSyntax syntax,
writer->sink = sink;
writer->stream = stream;
writer->context = context;
+ writer->list_subj = SERD_NODE_NULL;
+ writer->list_depth = 0;
writer->bprefix = NULL;
writer->bprefix_len = 0;
writer->indent = 0;
+ writer->last_sep = SEP_NONE;
writer->empty = true;
if (style & SERD_STYLE_BULK) {
writer->bulk_sink = serd_bulk_sink_new(sink, stream, SERD_PAGE_SIZE);