aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-02-25 20:08:20 -0500
committerDavid Robillard <d@drobilla.net>2021-03-08 23:23:05 -0500
commit01a76c7d703650df7f39d21c704f5b7c4f41ca14 (patch)
tree0d662aaceb9b6dee2cc2f566cd67aa20fa0939ca
parent3bf35cb9e72709fb417cff0d5a2dc9b681e5ecb1 (diff)
downloadserd-01a76c7d703650df7f39d21c704f5b7c4f41ca14.tar.gz
serd-01a76c7d703650df7f39d21c704f5b7c4f41ca14.tar.bz2
serd-01a76c7d703650df7f39d21c704f5b7c4f41ca14.zip
Add numeric node construction and access API
-rw-r--r--include/serd/serd.h63
-rw-r--r--src/node.c64
-rw-r--r--src/node.h5
-rw-r--r--src/string.c12
-rw-r--r--test/test_node.c213
5 files changed, 307 insertions, 50 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index e1a28fdf..2f349e90 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -315,18 +315,6 @@ size_t
serd_strlen(const char* SERD_NONNULL str, SerdNodeFlags* SERD_NULLABLE flags);
/**
- Parse a string to a double.
-
- The API of this function is identical to the standard C strtod function,
- except this function is locale-independent and always matches the lexical
- format used in the Turtle grammar (the decimal point is always ".").
-*/
-SERD_API
-double
-serd_strtod(const char* SERD_NONNULL str,
- const char* SERD_NONNULL* SERD_NULLABLE endptr);
-
-/**
Decode a base64 string.
This function can be used to deserialise a blob node created with
@@ -649,7 +637,7 @@ serd_new_double(double d);
Uses identical formatting to serd_new_double(), except with at most 9
significant digits (under 14 characters total).
- @param f Float value to serialise.
+ @param f Float value of literal.
@return A literal node with datatype xsd:float.
*/
SERD_API
@@ -657,9 +645,9 @@ SerdNode* SERD_ALLOCATED
serd_new_float(float f);
/**
- Create a new node by serialising `i` into an xsd:integer string
+ Create a new node by writing `i` as an xsd:integer string.
- @param i Integer value to serialise.
+ @param i Integer value of literal.
@param datatype Datatype of node, or NULL for xsd:integer.
*/
SERD_API
@@ -682,6 +670,51 @@ serd_new_blob(const void* SERD_NONNULL buf,
size_t size,
const SerdNode* SERD_NULLABLE datatype);
+/**
+ Return the value of `node` as a boolean.
+
+ This will work for booleans, and numbers of any datatype if they are 0 or
+ 1.
+
+ @return The value of `node` as a `bool`, or `false` on error.
+*/
+SERD_API
+bool
+serd_get_boolean(const SerdNode* SERD_NONNULL node);
+
+/**
+ Return the value of `node` as a double.
+
+ This will coerce numbers of any datatype to double, if the value fits.
+
+ @return The value of `node` as a `double`, or NaN on error.
+*/
+SERD_API
+double
+serd_get_double(const SerdNode* SERD_NONNULL node);
+
+/**
+ Return the value of `node` as a float.
+
+ This will coerce numbers of any datatype to float, if the value fits.
+
+ @return The value of `node` as a `float`, or NaN on error.
+*/
+SERD_API
+float
+serd_get_float(const SerdNode* SERD_NONNULL node);
+
+/**
+ Return the value of `node` as a long (signed 64-bit integer).
+
+ This will coerce numbers of any datatype to long, if the value fits.
+
+ @return The value of `node` as a `int64_t`, or 0 on error.
+*/
+SERD_API
+int64_t
+serd_get_integer(const SerdNode* SERD_NONNULL node);
+
/// Return a deep copy of `node`
SERD_API
SerdNode* SERD_ALLOCATED
diff --git a/src/node.c b/src/node.c
index db183fc0..f071fc97 100644
--- a/src/node.c
+++ b/src/node.c
@@ -276,6 +276,68 @@ serd_new_curie(const SerdStringView str)
return serd_new_simple_node(SERD_CURIE, str);
}
+ExessVariant
+serd_node_get_value_as(const SerdNode* const node,
+ const ExessDatatype value_type)
+{
+ const SerdNode* const datatype_node = serd_node_datatype(node);
+
+ const ExessDatatype node_type =
+ datatype_node ? exess_datatype_from_uri(serd_node_string(datatype_node))
+ : EXESS_NOTHING;
+
+ if (!node_type) {
+ // No datatype, assume it matches and try reading the value directly
+ ExessVariant v = exess_make_nothing(EXESS_SUCCESS);
+ const ExessResult r =
+ exess_read_variant(&v, value_type, serd_node_string(node));
+
+ return r.status ? exess_make_nothing(r.status) : v;
+ }
+
+ // Read the value from the node
+ ExessVariant v = exess_make_nothing(EXESS_SUCCESS);
+ const ExessResult r =
+ exess_read_variant(&v, node_type, serd_node_string(node));
+
+ // Coerce value to the desired type if possible
+ return r.status ? exess_make_nothing(r.status)
+ : exess_coerce(v, value_type, EXESS_REDUCE_PRECISION);
+}
+
+bool
+serd_get_boolean(const SerdNode* const node)
+{
+ const ExessVariant value = serd_node_get_value_as(node, EXESS_BOOLEAN);
+
+ return (value.datatype == EXESS_BOOLEAN) ? *exess_get_boolean(&value) : false;
+}
+
+double
+serd_get_double(const SerdNode* const node)
+{
+ const ExessVariant value = serd_node_get_value_as(node, EXESS_DOUBLE);
+
+ return (value.datatype == EXESS_DOUBLE) ? *exess_get_double(&value)
+ : (double)NAN;
+}
+
+float
+serd_get_float(const SerdNode* const node)
+{
+ const ExessVariant value = serd_node_get_value_as(node, EXESS_FLOAT);
+
+ return (value.datatype == EXESS_FLOAT) ? *exess_get_float(&value) : NAN;
+}
+
+int64_t
+serd_get_integer(const SerdNode* const node)
+{
+ const ExessVariant value = serd_node_get_value_as(node, EXESS_LONG);
+
+ return (value.datatype == EXESS_LONG) ? *exess_get_long(&value) : 0;
+}
+
SerdNode*
serd_node_copy(const SerdNode* node)
{
@@ -585,7 +647,7 @@ serd_new_decimal(const double d, const SerdNode* const datatype)
}
SerdNode*
-serd_new_integer(int64_t i, const SerdNode* datatype)
+serd_new_integer(const int64_t i, const SerdNode* datatype)
{
const ExessVariant variant = exess_make_long(i);
const size_t len = exess_write_variant(variant, 0, NULL).count;
diff --git a/src/node.h b/src/node.h
index 6bebdb6d..3d01bae7 100644
--- a/src/node.h
+++ b/src/node.h
@@ -17,6 +17,7 @@
#ifndef SERD_NODE_H
#define SERD_NODE_H
+#include "exess/exess.h"
#include "serd/serd.h"
#include <stddef.h>
@@ -53,4 +54,8 @@ serd_node_zero_pad(SerdNode* SERD_NONNULL node);
SerdNode* SERD_ALLOCATED
serd_new_resolved_uri(SerdStringView string, SerdURIView base_uri);
+ExessVariant
+serd_node_get_value_as(const SerdNode* SERD_NONNULL node,
+ ExessDatatype value_type);
+
#endif // SERD_NODE_H
diff --git a/src/string.c b/src/string.c
index 6e28778b..f59a5fd6 100644
--- a/src/string.c
+++ b/src/string.c
@@ -107,15 +107,3 @@ serd_strlen(const char* str, SerdNodeFlags* flags)
return strlen(str);
}
-
-double
-serd_strtod(const char* const str, const char** end)
-{
- double value = (double)NAN;
- const ExessResult r = exess_read_double(&value, str);
- if (end) {
- *end = str + r.count;
- }
-
- return r.status ? (double)NAN : value;
-}
diff --git a/test/test_node.c b/test/test_node.c
index 1e38ee9b..80c45b10 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -19,6 +19,7 @@
#include "serd/serd.h"
#include <assert.h>
+#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -28,45 +29,206 @@
#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
static void
-test_integer_to_node(void)
+test_boolean(void)
+{
+ SerdNode* const true_node = serd_new_boolean(true);
+ assert(!strcmp(serd_node_string(true_node), "true"));
+ assert(serd_get_boolean(true_node));
+
+ const SerdNode* const true_datatype = serd_node_datatype(true_node);
+ assert(true_datatype);
+ assert(!strcmp(serd_node_string(true_datatype), NS_XSD "boolean"));
+ serd_node_free(true_node);
+
+ SerdNode* const false_node = serd_new_boolean(false);
+ assert(!strcmp(serd_node_string(false_node), "false"));
+ assert(!serd_get_boolean(false_node));
+
+ const SerdNode* const false_datatype = serd_node_datatype(false_node);
+ assert(false_datatype);
+ assert(!strcmp(serd_node_string(false_datatype), NS_XSD "boolean"));
+ serd_node_free(false_node);
+}
+
+static void
+check_get_boolean(const char* string,
+ const char* datatype_uri,
+ const bool expected)
+{
+ SerdNode* const node = serd_new_typed_literal(
+ SERD_MEASURE_STRING(string), SERD_MEASURE_STRING(datatype_uri));
+
+ assert(node);
+ assert(serd_get_boolean(node) == expected);
+}
+
+static void
+test_get_boolean(void)
+{
+ check_get_boolean("false", NS_XSD "boolean", false);
+ check_get_boolean("true", NS_XSD "boolean", true);
+ check_get_boolean("0", NS_XSD "boolean", false);
+ check_get_boolean("1", NS_XSD "boolean", true);
+ check_get_boolean("0", NS_XSD "integer", false);
+ check_get_boolean("1", NS_XSD "integer", true);
+ check_get_boolean("0.0", NS_XSD "double", false);
+ check_get_boolean("1.0", NS_XSD "double", true);
+ check_get_boolean("unknown", NS_XSD "string", false);
+}
+
+static void
+test_double(void)
+{
+ const double test_values[] = {0.0, -0.0, 1.2, -2.3, 4567890};
+ const char* test_strings[] = {
+ "0.0E0", "-0.0E0", "1.2E0", "-2.3E0", "4.56789E6"};
+
+ for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
+ SerdNode* node = serd_new_double(test_values[i]);
+ const char* node_str = serd_node_string(node);
+ assert(!strcmp(node_str, test_strings[i]));
+
+ const size_t len = strlen(node_str);
+ assert(serd_node_length(node) == len);
+
+ const SerdNode* const datatype = serd_node_datatype(node);
+ assert(datatype);
+ assert(!strcmp(serd_node_string(datatype), NS_XSD "double"));
+
+ const double value = serd_get_double(node);
+ assert(!memcmp(&value, &test_values[i], sizeof(value)));
+ serd_node_free(node);
+ }
+}
+
+static void
+check_get_double(const char* string,
+ const char* datatype_uri,
+ const double expected)
+{
+ SerdNode* const node = serd_new_typed_literal(
+ SERD_MEASURE_STRING(string), SERD_MEASURE_STRING(datatype_uri));
+
+ assert(node);
+
+ const double value = serd_get_double(node);
+ assert(!memcmp(&value, &expected, sizeof(value)));
+}
+
+static void
+test_get_double(void)
{
- const long int_test_nums[] = {0, -0, -23, 23, -12340, 1000, -1000};
+ check_get_double("1.2", NS_XSD "double", 1.2);
+ check_get_double("-.5", NS_XSD "float", -0.5);
+ check_get_double("-67", NS_XSD "long", -67.0);
+ check_get_double("8.9", NS_XSD "decimal", 8.9);
+ check_get_double("false", NS_XSD "boolean", 0.0);
+ check_get_double("true", NS_XSD "boolean", 1.0);
+
+ SerdNode* const nan = serd_new_string(SERD_MEASURE_STRING("unknown"));
+ assert(isnan(serd_get_double(nan)));
+ serd_node_free(nan);
+}
+
+static void
+test_float(void)
+{
+ const float test_values[] = {0.0, -0.0, 1.5, -2.5, 4567890};
+ const char* test_strings[] = {
+ "0.0E0", "-0.0E0", "1.5E0", "-2.5E0", "4.56789E6"};
+
+ for (size_t i = 0; i < sizeof(test_values) / sizeof(float); ++i) {
+ SerdNode* node = serd_new_float(test_values[i]);
+ const char* node_str = serd_node_string(node);
+ assert(!strcmp(node_str, test_strings[i]));
+
+ const size_t len = strlen(node_str);
+ assert(serd_node_length(node) == len);
+
+ const SerdNode* const datatype = serd_node_datatype(node);
+ assert(datatype);
+ assert(!strcmp(serd_node_string(datatype), NS_XSD "float"));
- const char* int_test_strs[] = {
+ const float value = serd_get_float(node);
+ assert(!memcmp(&value, &test_values[i], sizeof(value)));
+ serd_node_free(node);
+ }
+}
+
+static void
+check_get_float(const char* string,
+ const char* datatype_uri,
+ const float expected)
+{
+ SerdNode* const node = serd_new_typed_literal(
+ SERD_MEASURE_STRING(string), SERD_MEASURE_STRING(datatype_uri));
+
+ assert(node);
+
+ const float value = serd_get_float(node);
+ assert(!memcmp(&value, &expected, sizeof(value)));
+}
+
+static void
+test_get_float(void)
+{
+ check_get_float("1.2", NS_XSD "float", 1.2f);
+ check_get_float("-.5", NS_XSD "float", -0.5f);
+ check_get_float("-67", NS_XSD "long", -67.0f);
+ check_get_float("1.5", NS_XSD "decimal", 1.5f);
+ check_get_float("false", NS_XSD "boolean", 0.0f);
+ check_get_float("true", NS_XSD "boolean", 1.0f);
+
+ SerdNode* const nan = serd_new_string(SERD_MEASURE_STRING("unknown"));
+ assert(isnan(serd_get_float(nan)));
+ serd_node_free(nan);
+}
+
+static void
+test_integer(void)
+{
+ const int64_t test_values[] = {0, -0, -23, 23, -12340, 1000, -1000};
+ const char* test_strings[] = {
"0", "0", "-23", "23", "-12340", "1000", "-1000"};
- for (size_t i = 0; i < sizeof(int_test_nums) / sizeof(double); ++i) {
- SerdNode* node = serd_new_integer(int_test_nums[i], NULL);
+ for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
+ SerdNode* node = serd_new_integer(test_values[i], NULL);
const char* node_str = serd_node_string(node);
- assert(!strcmp(node_str, int_test_strs[i]));
+ assert(!strcmp(node_str, test_strings[i]));
const size_t len = strlen(node_str);
assert(serd_node_length(node) == len);
const SerdNode* const datatype = serd_node_datatype(node);
assert(datatype);
assert(!strcmp(serd_node_string(datatype), NS_XSD "integer"));
+
+ assert(serd_get_integer(node) == test_values[i]);
serd_node_free(node);
}
}
static void
-test_boolean(void)
+check_get_integer(const char* string,
+ const char* datatype_uri,
+ const int64_t expected)
{
- SerdNode* const true_node = serd_new_boolean(true);
- assert(!strcmp(serd_node_string(true_node), "true"));
-
- const SerdNode* const true_datatype = serd_node_datatype(true_node);
- assert(true_datatype);
- assert(!strcmp(serd_node_string(true_datatype), NS_XSD "boolean"));
- serd_node_free(true_node);
+ SerdNode* const node = serd_new_typed_literal(
+ SERD_MEASURE_STRING(string), SERD_MEASURE_STRING(datatype_uri));
- SerdNode* const false_node = serd_new_boolean(false);
- assert(!strcmp(serd_node_string(false_node), "false"));
+ assert(node);
+ assert(serd_get_integer(node) == expected);
+}
- const SerdNode* const false_datatype = serd_node_datatype(false_node);
- assert(false_datatype);
- assert(!strcmp(serd_node_string(false_datatype), NS_XSD "boolean"));
- serd_node_free(false_node);
+static void
+test_get_integer(void)
+{
+ check_get_integer("12", NS_XSD "long", 12);
+ check_get_integer("-34", NS_XSD "long", -34);
+ check_get_integer("56", NS_XSD "integer", 56);
+ check_get_integer("false", NS_XSD "boolean", 0);
+ check_get_integer("true", NS_XSD "boolean", 1);
+ check_get_integer("78.0", NS_XSD "decimal", 78);
+ check_get_integer("unknown", NS_XSD "string", 0);
}
static void
@@ -114,6 +276,7 @@ test_node_equals(void)
SerdNode* lhs = serd_new_string(replacement_char);
SerdNode* rhs = serd_new_string(SERD_STATIC_STRING("123"));
+ assert(serd_node_equals(lhs, lhs));
assert(!serd_node_equals(lhs, rhs));
SerdNode* const qnode = serd_new_curie(SERD_STATIC_STRING("foo:bar"));
@@ -219,9 +382,15 @@ test_blank(void)
int
main(void)
{
- test_integer_to_node();
- test_blob_to_node();
test_boolean();
+ test_get_boolean();
+ test_double();
+ test_get_double();
+ test_float();
+ test_get_float();
+ test_integer();
+ test_get_integer();
+ test_blob_to_node();
test_node_equals();
test_node_from_string();
test_node_from_substring();