aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/conf.py.in1
-rw-r--r--include/serd/caret.h77
-rw-r--r--include/serd/error.h11
-rw-r--r--include/serd/reader.h8
-rw-r--r--include/serd/serd.h1
-rw-r--r--meson.build2
-rw-r--r--src/byte_source.c45
-rw-r--r--src/byte_source.h25
-rw-r--r--src/caret.c69
-rw-r--r--src/caret.h15
-rw-r--r--src/reader.c31
-rw-r--r--src/serdi.c12
-rw-r--r--src/world.c18
-rw-r--r--test/meson.build1
-rw-r--r--test/test_caret.c49
-rw-r--r--test/test_free_null.c2
-rw-r--r--test/test_overflow.c2
-rw-r--r--test/test_reader_writer.c5
18 files changed, 310 insertions, 64 deletions
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 5f0c026a..9b1cdc70 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -27,6 +27,7 @@ except ModuleNotFoundError:
# Ignore everything opaque or external for nitpicky mode
_opaque = [
"FILE",
+ "SerdCaretImpl",
"SerdEnvImpl",
"SerdNodeImpl",
"SerdReaderImpl",
diff --git a/include/serd/caret.h b/include/serd/caret.h
new file mode 100644
index 00000000..2ed77412
--- /dev/null
+++ b/include/serd/caret.h
@@ -0,0 +1,77 @@
+// Copyright 2011-2022 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#ifndef SERD_CARET_H
+#define SERD_CARET_H
+
+#include "serd/attributes.h"
+#include "serd/node.h"
+
+#include <stdbool.h>
+
+SERD_BEGIN_DECLS
+
+/**
+ @defgroup serd_caret Caret
+ @ingroup serd_data
+ @{
+*/
+
+/// The origin of a statement in a text document
+typedef struct SerdCaretImpl SerdCaret;
+
+/**
+ Create a new caret.
+
+ Note that, to minimise model overhead, the caret does not own the document
+ node, so `document` must have a longer lifetime than the caret for it to be
+ valid. That is, serd_caret_document() will return exactly the pointer
+ `document`, not a copy.
+
+ @param document The document or the caret refers to (usually a file URI)
+ @param line The line number in the document (1-based)
+ @param column The column number in the document (1-based)
+ @return A new caret that must be freed with serd_caret_free()
+*/
+SERD_API SerdCaret* SERD_ALLOCATED
+serd_caret_new(const SerdNode* SERD_NONNULL document,
+ unsigned line,
+ unsigned column);
+
+/// Return a copy of `caret`
+SERD_API SerdCaret* SERD_ALLOCATED
+serd_caret_copy(const SerdCaret* SERD_NULLABLE caret);
+
+/// Free `caret`
+SERD_API void
+serd_caret_free(SerdCaret* SERD_NULLABLE caret);
+
+/// Return true iff `lhs` is equal to `rhs`
+SERD_PURE_API bool
+serd_caret_equals(const SerdCaret* SERD_NULLABLE lhs,
+ const SerdCaret* SERD_NULLABLE rhs);
+
+/**
+ Return the document URI or name.
+
+ This is typically a file URI, but may be a descriptive string node for
+ statements that originate from streams.
+*/
+SERD_PURE_API const SerdNode* SERD_NONNULL
+serd_caret_document(const SerdCaret* SERD_NONNULL caret);
+
+/// Return the one-relative line number in the document
+SERD_PURE_API unsigned
+serd_caret_line(const SerdCaret* SERD_NONNULL caret);
+
+/// Return the zero-relative column number in the line
+SERD_PURE_API unsigned
+serd_caret_column(const SerdCaret* SERD_NONNULL caret);
+
+/**
+ @}
+*/
+
+SERD_END_DECLS
+
+#endif // SERD_CARET_H
diff --git a/include/serd/error.h b/include/serd/error.h
index 7be1d372..7495ce3b 100644
--- a/include/serd/error.h
+++ b/include/serd/error.h
@@ -5,6 +5,7 @@
#define SERD_ERROR_H
#include "serd/attributes.h"
+#include "serd/caret.h"
#include "serd/status.h"
#include <stdarg.h>
@@ -19,12 +20,10 @@ SERD_BEGIN_DECLS
/// An error description
typedef struct {
- SerdStatus status; ///< Error code
- const char* SERD_NULLABLE filename; ///< File with error
- unsigned line; ///< Line in file with error or 0
- unsigned col; ///< Column in file with error
- const char* SERD_NONNULL fmt; ///< Printf-style format string
- va_list* SERD_NONNULL args; ///< Arguments for fmt
+ SerdStatus status; ///< Error code
+ const SerdCaret* SERD_NULLABLE caret; ///< File origin of error
+ const char* SERD_NONNULL fmt; ///< Printf-style format string
+ va_list* SERD_NONNULL args; ///< Arguments for fmt
} SerdError;
/**
diff --git a/include/serd/reader.h b/include/serd/reader.h
index aedc5cb3..cc19a9b8 100644
--- a/include/serd/reader.h
+++ b/include/serd/reader.h
@@ -5,6 +5,7 @@
#define SERD_READER_H
#include "serd/attributes.h"
+#include "serd/node.h"
#include "serd/sink.h"
#include "serd/status.h"
#include "serd/stream.h"
@@ -73,13 +74,14 @@ serd_reader_start_stream(SerdReader* SERD_NONNULL reader,
SerdReadFunc SERD_NONNULL read_func,
SerdStreamErrorFunc SERD_NONNULL error_func,
void* SERD_NONNULL stream,
- const char* SERD_NULLABLE name,
+ const SerdNode* SERD_NULLABLE name,
size_t page_size);
/// Prepare to read from a string
SERD_API SerdStatus
-serd_reader_start_string(SerdReader* SERD_NONNULL reader,
- const char* SERD_NONNULL utf8);
+serd_reader_start_string(SerdReader* SERD_NONNULL reader,
+ const char* SERD_NONNULL utf8,
+ const SerdNode* SERD_NULLABLE name);
/**
Read a single "chunk" of data during an incremental read.
diff --git a/include/serd/serd.h b/include/serd/serd.h
index eb33d329..8de151c6 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -57,6 +57,7 @@
@{
*/
+#include "serd/caret.h"
#include "serd/node.h"
#include "serd/statement.h"
#include "serd/uri.h"
diff --git a/meson.build b/meson.build
index fa183942..8b4652b1 100644
--- a/meson.build
+++ b/meson.build
@@ -121,6 +121,7 @@ include_dirs = include_directories('include')
c_headers = files(
'include/serd/attributes.h',
'include/serd/buffer.h',
+ 'include/serd/caret.h',
'include/serd/env.h',
'include/serd/error.h',
'include/serd/memory.h',
@@ -143,6 +144,7 @@ c_headers = files(
sources = files(
'src/byte_source.c',
+ 'src/caret.c',
'src/env.c',
'src/n3.c',
'src/node.c',
diff --git a/src/byte_source.c b/src/byte_source.c
index d6510eb9..8e44d891 100644
--- a/src/byte_source.c
+++ b/src/byte_source.c
@@ -5,6 +5,9 @@
#include "system.h"
+#include "serd/node.h"
+#include "serd/string_view.h"
+
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@@ -37,20 +40,21 @@ serd_byte_source_open_source(SerdByteSource* const source,
const SerdStreamErrorFunc error_func,
const SerdStreamCloseFunc close_func,
void* const stream,
- const char* const name,
+ const SerdNode* const name,
const size_t page_size)
{
- const Cursor cur = {name, 1, 1};
-
memset(source, '\0', sizeof(*source));
- source->read_func = read_func;
- source->error_func = error_func;
- source->close_func = close_func;
- source->stream = stream;
- source->page_size = page_size;
- source->buf_size = page_size;
- source->cur = cur;
- source->from_stream = true;
+ source->read_func = read_func;
+ source->error_func = error_func;
+ source->close_func = close_func;
+ source->stream = stream;
+ source->page_size = page_size;
+ source->buf_size = page_size;
+ source->name = serd_node_copy(name);
+ source->caret.document = source->name;
+ source->caret.line = 1U;
+ source->caret.col = 1U;
+ source->from_stream = true;
if (page_size > 1) {
source->file_buf = (uint8_t*)serd_allocate_buffer(page_size);
@@ -82,14 +86,20 @@ serd_byte_source_prepare(SerdByteSource* const source)
SerdStatus
serd_byte_source_open_string(SerdByteSource* const source,
- const char* const utf8)
+ const char* const utf8,
+ const SerdNode* const name)
{
- const Cursor cur = {"(string)", 1, 1};
-
memset(source, '\0', sizeof(*source));
- source->page_size = 1;
- source->cur = cur;
- source->read_buf = (const uint8_t*)utf8;
+
+ source->name =
+ name ? serd_node_copy(name) : serd_new_string(serd_string("string"));
+
+ source->page_size = 1U;
+ source->read_buf = (const uint8_t*)utf8;
+ source->caret.document = source->name;
+ source->caret.line = 1U;
+ source->caret.col = 1U;
+
return SERD_SUCCESS;
}
@@ -105,6 +115,7 @@ serd_byte_source_close(SerdByteSource* const source)
serd_free_aligned(source->file_buf);
}
+ serd_node_free(source->name);
memset(source, '\0', sizeof(*source));
return st;
}
diff --git a/src/byte_source.h b/src/byte_source.h
index e2697bd2..cc6bc840 100644
--- a/src/byte_source.h
+++ b/src/byte_source.h
@@ -4,7 +4,11 @@
#ifndef SERD_SRC_BYTE_SOURCE_H
#define SERD_SRC_BYTE_SOURCE_H
+#include "caret.h" // IWYU pragma: keep
+
#include "serd/attributes.h"
+#include "serd/caret.h"
+#include "serd/node.h"
#include "serd/status.h"
#include "serd/stream.h"
@@ -16,19 +20,14 @@
typedef int (*SerdStreamCloseFunc)(void*);
typedef struct {
- const char* filename;
- unsigned line;
- unsigned col;
-} Cursor;
-
-typedef struct {
SerdReadFunc read_func; ///< Read function (e.g. fread)
SerdStreamErrorFunc error_func; ///< Error function (e.g. ferror)
SerdStreamCloseFunc close_func; ///< Function for closing stream
void* stream; ///< Stream (e.g. FILE)
size_t page_size; ///< Number of bytes to read at a time
size_t buf_size; ///< Number of bytes in file_buf
- Cursor cur; ///< Cursor for error reporting
+ SerdNode* name; ///< Name of stream (referenced by cur)
+ SerdCaret caret; ///< Caret for error reporting
uint8_t* file_buf; ///< Buffer iff reading pages from a file
const uint8_t* read_buf; ///< Pointer to file_buf or read_byte
size_t read_head; ///< Offset into read_buf
@@ -39,7 +38,9 @@ typedef struct {
} SerdByteSource;
SerdStatus
-serd_byte_source_open_string(SerdByteSource* source, const char* utf8);
+serd_byte_source_open_string(SerdByteSource* source,
+ const char* utf8,
+ const SerdNode* name);
SerdStatus
serd_byte_source_open_source(SerdByteSource* source,
@@ -47,7 +48,7 @@ serd_byte_source_open_source(SerdByteSource* source,
SerdStreamErrorFunc error_func,
SerdStreamCloseFunc close_func,
void* stream,
- const char* name,
+ const SerdNode* name,
size_t page_size);
SerdStatus
@@ -73,11 +74,11 @@ serd_byte_source_advance(SerdByteSource* source)
switch (serd_byte_source_peek(source)) {
case '\n':
- ++source->cur.line;
- source->cur.col = 0;
+ ++source->caret.line;
+ source->caret.col = 0;
break;
default:
- ++source->cur.col;
+ ++source->caret.col;
}
const bool was_eof = source->eof;
diff --git a/src/caret.c b/src/caret.c
new file mode 100644
index 00000000..1b0de444
--- /dev/null
+++ b/src/caret.c
@@ -0,0 +1,69 @@
+// Copyright 2018-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#include "caret.h"
+
+#include "serd/caret.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+SerdCaret*
+serd_caret_new(const SerdNode* const document,
+ const unsigned line,
+ const unsigned column)
+{
+ SerdCaret* caret = (SerdCaret*)malloc(sizeof(SerdCaret));
+
+ if (caret) {
+ caret->document = document;
+ caret->line = line;
+ caret->col = column;
+ }
+
+ return caret;
+}
+
+SerdCaret*
+serd_caret_copy(const SerdCaret* const caret)
+{
+ if (!caret) {
+ return NULL;
+ }
+
+ SerdCaret* copy = (SerdCaret*)malloc(sizeof(SerdCaret));
+ memcpy(copy, caret, sizeof(SerdCaret));
+ return copy;
+}
+
+void
+serd_caret_free(SerdCaret* const caret)
+{
+ free(caret);
+}
+
+bool
+serd_caret_equals(const SerdCaret* const l, const SerdCaret* const r)
+{
+ return (l == r || (l && r && serd_node_equals(l->document, r->document) &&
+ l->line == r->line && l->col == r->col));
+}
+
+const SerdNode*
+serd_caret_document(const SerdCaret* const caret)
+{
+ return caret->document;
+}
+
+unsigned
+serd_caret_line(const SerdCaret* const caret)
+{
+ return caret->line;
+}
+
+unsigned
+serd_caret_column(const SerdCaret* const caret)
+{
+ return caret->col;
+}
diff --git a/src/caret.h b/src/caret.h
new file mode 100644
index 00000000..85b4b4b1
--- /dev/null
+++ b/src/caret.h
@@ -0,0 +1,15 @@
+// Copyright 2018-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#ifndef SERD_SRC_CARET_H
+#define SERD_SRC_CARET_H
+
+#include "serd/node.h"
+
+struct SerdCaretImpl {
+ const SerdNode* document;
+ unsigned line;
+ unsigned col;
+};
+
+#endif // SERD_SRC_CARET_H
diff --git a/src/reader.c b/src/reader.c
index 2ef3b66a..a9c483c0 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -11,6 +11,7 @@
#include "world.h"
#include "serd/stream.h"
+#include "serd/string_view.h"
#include "serd/uri.h"
#include <errno.h>
@@ -27,8 +28,7 @@ r_err(SerdReader* const reader, const SerdStatus st, const char* const fmt, ...)
{
va_list args; // NOLINT(cppcoreguidelines-init-variables)
va_start(args, fmt);
- const Cursor* const cur = &reader->source.cur;
- const SerdError e = {st, cur->filename, cur->line, cur->col, fmt, &args};
+ const SerdError e = {st, &reader->source.caret, fmt, &args};
serd_world_error(reader->world, &e);
va_end(args);
return st;
@@ -230,7 +230,7 @@ serd_reader_start_stream(SerdReader* const reader,
const SerdReadFunc read_func,
const SerdStreamErrorFunc error_func,
void* const stream,
- const char* const name,
+ const SerdNode* const name,
const size_t page_size)
{
return serd_byte_source_open_source(
@@ -251,20 +251,25 @@ serd_reader_start_file(SerdReader* reader, const char* uri, bool bulk)
return SERD_BAD_STREAM;
}
- return serd_byte_source_open_source(&reader->source,
- bulk ? (SerdReadFunc)fread
- : serd_file_read_byte,
- (SerdStreamErrorFunc)ferror,
- (SerdStreamCloseFunc)fclose,
- fd,
- uri,
- bulk ? SERD_PAGE_SIZE : 1);
+ SerdNode* const name = serd_new_uri(serd_string(uri));
+ const SerdStatus st = serd_byte_source_open_source(
+ &reader->source,
+ bulk ? (SerdReadFunc)fread : serd_file_read_byte,
+ (SerdStreamErrorFunc)ferror,
+ (SerdStreamCloseFunc)fclose,
+ fd,
+ name,
+ bulk ? SERD_PAGE_SIZE : 1U);
+ serd_node_free(name);
+ return st;
}
SerdStatus
-serd_reader_start_string(SerdReader* const reader, const char* const utf8)
+serd_reader_start_string(SerdReader* const reader,
+ const char* const utf8,
+ const SerdNode* const name)
{
- return serd_byte_source_open_string(&reader->source, utf8);
+ return serd_byte_source_open_string(&reader->source, utf8, name);
}
static SerdStatus
diff --git a/src/serdi.c b/src/serdi.c
index 4649f57a..0932348c 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -298,15 +298,18 @@ main(int argc, char** argv)
serd_writer_chop_blank_prefix(writer, chop_prefix);
serd_reader_add_blank_prefix(reader, add_prefix);
- SerdStatus st = SERD_SUCCESS;
+ SerdStatus st = SERD_SUCCESS;
+ SerdNode* input_name = NULL;
if (from_string) {
- st = serd_reader_start_string(reader, input);
+ input_name = serd_new_string(serd_string("string"));
+ st = serd_reader_start_string(reader, input, input_name);
} else if (from_stdin) {
- st = serd_reader_start_stream(reader,
+ input_name = serd_new_string(serd_string("stdin"));
+ st = serd_reader_start_stream(reader,
serd_file_read_byte,
(SerdStreamErrorFunc)ferror,
stdin,
- "(stdin)",
+ input_name,
1);
} else {
st = serd_reader_start_file(reader, input, bulk_read);
@@ -320,6 +323,7 @@ main(int argc, char** argv)
serd_reader_free(reader);
serd_writer_finish(writer);
serd_writer_free(writer);
+ serd_node_free(input_name);
serd_env_free(env);
serd_node_free(base);
serd_world_free(world);
diff --git a/src/world.c b/src/world.c
index ececd646..21cdffab 100644
--- a/src/world.c
+++ b/src/world.c
@@ -3,10 +3,13 @@
#include "world.h"
+#include "caret.h"
#include "node.h"
#include "serd_config.h"
#include "system.h"
+#include "serd/node.h"
+
#include "serd/string_view.h"
#if defined(USE_POSIX_FADVISE)
@@ -45,10 +48,13 @@ serd_world_error(const SerdWorld* const world, const SerdError* const e)
if (world->error_func) {
world->error_func(world->error_handle, e);
} else {
- if (e->filename) {
- fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col);
- } else {
- fprintf(stderr, "error: ");
+ fprintf(stderr, "error: ");
+ if (e->caret) {
+ fprintf(stderr,
+ "%s:%u:%u: ",
+ serd_node_string(e->caret->document),
+ e->caret->line,
+ e->caret->col);
}
vfprintf(stderr, e->fmt, *e->args);
}
@@ -64,7 +70,7 @@ serd_world_verrorf(const SerdWorld* const world,
va_list args_copy;
va_copy(args_copy, args);
- const SerdError e = {st, NULL, 0, 0, fmt, &args_copy};
+ const SerdError e = {st, NULL, fmt, &args_copy};
serd_world_error(world, &e);
va_end(args_copy);
return st;
@@ -78,7 +84,7 @@ serd_world_errorf(const SerdWorld* const world,
{
va_list args; // NOLINT(cppcoreguidelines-init-variables)
va_start(args, fmt);
- const SerdError e = {st, NULL, 0, 0, fmt, &args};
+ const SerdError e = {st, NULL, fmt, &args};
serd_world_error(world, &e);
va_end(args);
return st;
diff --git a/test/meson.build b/test/meson.build
index efcb00f0..c3f29637 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -119,6 +119,7 @@ subdir('headers')
##############
unit_tests = [
+ 'caret',
'env',
'free_null',
'node',
diff --git a/test/test_caret.c b/test/test_caret.c
new file mode 100644
index 00000000..a239c242
--- /dev/null
+++ b/test/test_caret.c
@@ -0,0 +1,49 @@
+// Copyright 2019-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#undef NDEBUG
+
+#include "serd/caret.h"
+#include "serd/node.h"
+#include "serd/string_view.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+int
+main(void)
+{
+ SerdNode* const node = serd_new_string(serd_string("node"));
+ SerdCaret* const caret = serd_caret_new(node, 46, 2);
+
+ assert(serd_caret_equals(caret, caret));
+ assert(serd_caret_document(caret) == node);
+ assert(serd_caret_line(caret) == 46);
+ assert(serd_caret_column(caret) == 2);
+
+ SerdCaret* const copy = serd_caret_copy(caret);
+
+ assert(serd_caret_equals(caret, copy));
+ assert(!serd_caret_copy(NULL));
+
+ SerdNode* const other_node = serd_new_string(serd_string("other"));
+ SerdCaret* const other_file = serd_caret_new(other_node, 46, 2);
+ SerdCaret* const other_line = serd_caret_new(node, 47, 2);
+ SerdCaret* const other_col = serd_caret_new(node, 46, 3);
+
+ assert(!serd_caret_equals(caret, other_file));
+ assert(!serd_caret_equals(caret, other_line));
+ assert(!serd_caret_equals(caret, other_col));
+ assert(!serd_caret_equals(caret, NULL));
+ assert(!serd_caret_equals(NULL, caret));
+
+ serd_caret_free(other_col);
+ serd_caret_free(other_line);
+ serd_caret_free(other_file);
+ serd_node_free(other_node);
+ serd_caret_free(copy);
+ serd_caret_free(caret);
+ serd_node_free(node);
+
+ return 0;
+}
diff --git a/test/test_free_null.c b/test/test_free_null.c
index 72783328..87dfa7db 100644
--- a/test/test_free_null.c
+++ b/test/test_free_null.c
@@ -3,6 +3,7 @@
#undef NDEBUG
+#include "serd/caret.h"
#include "serd/env.h"
#include "serd/memory.h"
#include "serd/node.h"
@@ -23,6 +24,7 @@ main(void)
serd_sink_free(NULL);
serd_reader_free(NULL);
serd_writer_free(NULL);
+ serd_caret_free(NULL);
return 0;
}
diff --git a/test/test_overflow.c b/test/test_overflow.c
index a304ae1c..f4c5a863 100644
--- a/test/test_overflow.c
+++ b/test/test_overflow.c
@@ -23,7 +23,7 @@ test_size(SerdWorld* const world,
return SERD_BAD_STACK;
}
- serd_reader_start_string(reader, str);
+ serd_reader_start_string(reader, str, NULL);
const SerdStatus st = serd_reader_read_document(reader);
serd_reader_free(reader);
serd_sink_free(sink);
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 062f65fc..39bde443 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -370,7 +370,8 @@ test_read_string(void)
assert(
!serd_reader_start_string(reader,
"<http://example.org/s> <http://example.org/p> "
- "<http://example.org/o> ."));
+ "<http://example.org/o> .",
+ NULL));
assert(!serd_reader_read_document(reader));
assert(rt->n_base == 0);
@@ -433,7 +434,7 @@ test_write_errors(void)
const SerdSink* const sink = serd_writer_sink(writer);
SerdReader* const reader = serd_reader_new(world, SERD_TRIG, sink, 4096U);
- SerdStatus st = serd_reader_start_string(reader, doc_string);
+ SerdStatus st = serd_reader_start_string(reader, doc_string, NULL);
assert(!st);
st = serd_reader_read_document(reader);
assert(st == SERD_BAD_WRITE);