aboutsummaryrefslogtreecommitdiffstats
path: root/src/writer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/writer.c')
-rw-r--r--src/writer.c227
1 files changed, 200 insertions, 27 deletions
diff --git a/src/writer.c b/src/writer.c
index ef10594b..684f79e2 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -21,16 +21,48 @@
#include "serd/serd.h"
+typedef bool (*StatementWriter)(SerdWriter writer,
+ const SerdString* graph,
+ const SerdString* subject,
+ SerdNodeType subject_type,
+ const SerdString* predicate,
+ SerdNodeType predicate_type,
+ const SerdString* object,
+ SerdNodeType object_type,
+ const SerdString* object_datatype,
+ const SerdString* object_lang);
+
+typedef bool (*NodeWriter)(SerdWriter writer,
+ SerdNodeType type,
+ const SerdString* str,
+ const SerdString* datatype,
+ const SerdString* lang);
+
struct SerdWriterImpl {
- SerdSyntax syntax;
- SerdNamespaces ns;
- SerdURI base_uri;
- SerdSink sink;
- void* stream;
+ SerdSyntax syntax;
+ SerdStyle style;
+ SerdNamespaces ns;
+ SerdURI base_uri;
+ SerdSink sink;
+ void* stream;
+ StatementWriter write_statement;
+ NodeWriter write_node;
+ const SerdString* prev_g;
+ const SerdString* prev_s;
+ const SerdString* prev_p;
+ const SerdString* prev_o;
+ unsigned indent;
};
+typedef enum {
+ WRITE_NORMAL,
+ WRITE_URI,
+ WRITE_STRING
+} WriteContext;
+
static bool
-serd_write_ascii(SerdWriter writer, const uint8_t* utf8, size_t n_bytes, const uint8_t esc)
+write_text(SerdWriter writer, WriteContext ctx,
+ const uint8_t* utf8, size_t n_bytes, uint8_t terminator)
{
char escape[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (size_t i = 0; i < n_bytes;) {
@@ -40,12 +72,16 @@ serd_write_ascii(SerdWriter writer, const uint8_t* utf8, size_t n_bytes, const u
case '\n': writer->sink("\\n", 2, writer->stream); continue;
case '\r': writer->sink("\\r", 2, writer->stream); continue;
case '\t': writer->sink("\\t", 2, writer->stream); continue;
- case '"': if (esc == '"') { writer->sink("\\\"", 2, writer->stream); continue; }
+ case '"':
+ if (terminator == '"') {
+ writer->sink("\\\"", 2, writer->stream);
+ continue;
+ } // else fall-through
default: break;
}
- if (in == esc) {
- sprintf(escape, "\\u%04X", esc);
+ if (in == terminator) {
+ sprintf(escape, "\\u%04X", terminator);
writer->sink(escape, 6, writer->stream);
continue;
}
@@ -79,6 +115,13 @@ serd_write_ascii(SerdWriter writer, const uint8_t* utf8, size_t n_bytes, const u
return false;
}
+ if (!(writer->style & SERD_STYLE_ASCII)) {
+ // Write UTF-8 input directly to UTF-8 output
+ writer->sink(utf8, n_bytes, writer->stream);
+ i += n_bytes - 1;
+ continue;
+ }
+
#define READ_BYTE() do { \
assert(i < n_bytes); \
in = utf8[i++] & 0x3f; \
@@ -106,11 +149,11 @@ serd_write_ascii(SerdWriter writer, const uint8_t* utf8, size_t n_bytes, const u
}
static bool
-serd_write_node(SerdWriter writer,
- SerdNodeType type,
- const SerdString* str,
- const SerdString* datatype,
- const SerdString* lang)
+write_node(SerdWriter writer,
+ SerdNodeType type,
+ const SerdString* str,
+ const SerdString* datatype,
+ const SerdString* lang)
{
const SerdURI* base_uri = &writer->base_uri;
SerdNamespaces ns = writer->ns;
@@ -123,14 +166,20 @@ serd_write_node(SerdWriter writer,
writer->sink(str->buf, str->n_bytes - 1, writer->stream);
break;
case QNAME:
- if (!serd_namespaces_expand(ns, str, &uri_prefix, &uri_suffix)) {
- fprintf(stderr, "error: undefined namespace prefix `%s'\n", str->buf);
- return false;
+ switch (writer->syntax) {
+ case SERD_NTRIPLES:
+ if (!serd_namespaces_expand(ns, str, &uri_prefix, &uri_suffix)) {
+ fprintf(stderr, "error: undefined namespace prefix `%s'\n", str->buf);
+ return false;
+ }
+ writer->sink("<", 1, writer->stream);
+ write_text(writer, WRITE_URI, uri_prefix.buf, uri_prefix.len, '>');
+ write_text(writer, WRITE_URI, uri_suffix.buf, uri_suffix.len, '>');
+ writer->sink(">", 1, writer->stream);
+ break;
+ case SERD_TURTLE:
+ writer->sink(str->buf, str->n_bytes - 1, writer->stream);
}
- writer->sink("<", 1, writer->stream);
- serd_write_ascii(writer, uri_prefix.buf, uri_prefix.len, '>');
- serd_write_ascii(writer, uri_suffix.buf, uri_suffix.len, '>');
- writer->sink(">", 1, writer->stream);
break;
case URI:
if (!serd_uri_string_has_scheme(str->buf)) {
@@ -146,27 +195,44 @@ serd_write_node(SerdWriter writer,
}
} else {
writer->sink("<", 1, writer->stream);
- serd_write_ascii(writer, str->buf, str->n_bytes - 1, '>');
+ write_text(writer, WRITE_URI, str->buf, str->n_bytes - 1, '>');
writer->sink(">", 1, writer->stream);
return true;
}
return false;
case LITERAL:
writer->sink("\"", 1, writer->stream);
- serd_write_ascii(writer, str->buf, str->n_bytes - 1, '"');
+ write_text(writer, WRITE_STRING, str->buf, str->n_bytes - 1, '"');
writer->sink("\"", 1, writer->stream);
if (lang) {
writer->sink("@", 1, writer->stream);
writer->sink(lang->buf, lang->n_bytes - 1, writer->stream);
} else if (datatype) {
writer->sink("^^", 2, writer->stream);
- serd_write_node(writer, URI, datatype, NULL, NULL);
+ write_node(writer, URI, datatype, NULL, NULL);
}
break;
}
return true;
}
+static void
+serd_writer_write_delim(SerdWriter writer, const uint8_t delim)
+{
+ switch (delim) {
+ case 0:
+ case '\n':
+ break;
+ default:
+ writer->sink(" ", 1, writer->stream);
+ writer->sink(&delim, 1, writer->stream);
+ }
+ writer->sink("\n", 1, writer->stream);
+ for (unsigned i = 0; i < writer->indent; ++i) {
+ writer->sink("\t", 1, writer->stream);
+ }
+}
+
SERD_API
bool
serd_writer_write_statement(SerdWriter writer,
@@ -180,18 +246,98 @@ serd_writer_write_statement(SerdWriter writer,
const SerdString* object_datatype,
const SerdString* object_lang)
{
- serd_write_node(writer, subject_type, subject, NULL, NULL);
+ return writer->write_statement(writer,
+ graph,
+ subject, subject_type,
+ predicate, predicate_type,
+ object, object_type, object_datatype, object_lang);
+}
+
+static bool
+serd_writer_write_statement_abbrev(SerdWriter writer,
+ const SerdString* graph,
+ const SerdString* subject,
+ SerdNodeType subject_type,
+ const SerdString* predicate,
+ SerdNodeType predicate_type,
+ const SerdString* object,
+ SerdNodeType object_type,
+ const SerdString* object_datatype,
+ const SerdString* object_lang)
+{
+ assert(subject && predicate && object);
+ if (subject == writer->prev_s) {
+ if (predicate == writer->prev_p) {
+ ++writer->indent;
+ serd_writer_write_delim(writer, ',');
+ write_node(writer, object_type, object, object_datatype, object_lang);
+ --writer->indent;
+ } else {
+ serd_writer_write_delim(writer, ';');
+ write_node(writer, predicate_type, predicate, NULL, NULL);
+ writer->sink(" ", 1, writer->stream);
+ write_node(writer, object_type, object, object_datatype, object_lang);
+ }
+ } else {
+ if (writer->prev_s) {
+ --writer->indent;
+ serd_writer_write_delim(writer, '.');
+ serd_writer_write_delim(writer, '\n');
+ }
+ write_node(writer, subject_type, subject, NULL, NULL);
+ ++writer->indent;
+ serd_writer_write_delim(writer, 0);
+
+ writer->sink(" ", 1, writer->stream);
+ write_node(writer, predicate_type, predicate, NULL, NULL);
+ writer->sink(" ", 1, writer->stream);
+
+ write_node(writer, object_type, object, object_datatype, object_lang);
+ }
+
+ writer->prev_g = graph;
+ writer->prev_s = subject;
+ writer->prev_p = predicate;
+ writer->prev_o = object;
+ return true;
+}
+
+SERD_API
+bool
+serd_writer_write_statement_flat(SerdWriter writer,
+ const SerdString* graph,
+ const SerdString* subject,
+ SerdNodeType subject_type,
+ const SerdString* predicate,
+ SerdNodeType predicate_type,
+ const SerdString* object,
+ SerdNodeType object_type,
+ const SerdString* object_datatype,
+ const SerdString* object_lang)
+{
+ assert(subject && predicate && object);
+ write_node(writer, subject_type, subject, NULL, NULL);
writer->sink(" ", 1, writer->stream);
- serd_write_node(writer, predicate_type, predicate, NULL, NULL);
+ write_node(writer, predicate_type, predicate, NULL, NULL);
writer->sink(" ", 1, writer->stream);
- serd_write_node(writer, object_type, object, object_datatype, object_lang);
+ write_node(writer, object_type, object, object_datatype, object_lang);
writer->sink(" .\n", 3, writer->stream);
return true;
}
SERD_API
+void
+serd_writer_finish(SerdWriter writer)
+{
+ if (writer->prev_s) {
+ writer->sink(" .\n", 3, writer->stream);
+ }
+}
+
+SERD_API
SerdWriter
serd_writer_new(SerdSyntax syntax,
+ SerdStyle style,
SerdNamespaces ns,
const SerdURI* base_uri,
SerdSink sink,
@@ -199,10 +345,22 @@ serd_writer_new(SerdSyntax syntax,
{
SerdWriter writer = malloc(sizeof(struct SerdWriterImpl));
writer->syntax = syntax;
+ writer->style = style;
writer->ns = ns;
writer->base_uri = *base_uri;
writer->sink = sink;
writer->stream = stream;
+ writer->prev_g = 0;
+ writer->prev_s = 0;
+ writer->prev_p = 0;
+ writer->prev_o = 0;
+ writer->indent = 0;
+ writer->write_node = write_node;
+ if ((style & SERD_STYLE_ABBREVIATED)) {
+ writer->write_statement = serd_writer_write_statement_abbrev;
+ } else {
+ writer->write_statement = serd_writer_write_statement_flat;
+ }
return writer;
}
@@ -216,6 +374,21 @@ serd_writer_set_base_uri(SerdWriter writer,
SERD_API
void
+serd_writer_set_prefix(SerdWriter writer,
+ const SerdString* name,
+ const SerdString* uri)
+{
+ if (writer->syntax != SERD_NTRIPLES) {
+ writer->sink("@prefix ", 8, writer->stream);
+ writer->sink(name->buf, name->n_bytes - 1, writer->stream);
+ writer->sink(": <", 3, writer->stream);
+ write_text(writer, WRITE_URI, uri->buf, uri->n_bytes - 1, '>');
+ writer->sink("> .\n", 4, writer->stream);
+ }
+}
+
+SERD_API
+void
serd_writer_free(SerdWriter writer)
{
SerdWriter const me = (SerdWriter)writer;