Program Listing for File Literal.cpp¶
↰ Return to documentation for file (src/rdf4cpp/Literal.cpp)
#include "Literal.hpp"
#include <algorithm>
#include <execution>
#include <random>
#include <ranges>
#include <sstream>
#include <array>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <uni_algo/all.h>
#include <rdf4cpp/writer/BufWriter.hpp>
#include <rdf4cpp/writer/TryWrite.hpp>
#include <rdf4cpp/IRI.hpp>
#include <rdf4cpp/datatypes/registry/util/DateTimeUtils.hpp>
#include <rdf4cpp/util/CaseInsensitiveCharTraits.hpp>
#include <rdf4cpp/writer/Prefixes.hpp>
#include <rdf4cpp/util/CharMatcher.hpp>
#include <openssl/evp.h>
namespace rdf4cpp {
bool Literal::lexical_form_needs_escape(std::string_view const lexical_form) noexcept {
static constexpr datatypes::registry::util::ConstexprString pattern = "\"\\\n\r";
return util::char_matcher_detail::contains_any(lexical_form, pattern);
}
template<typename T, typename S>
static std::string run_serialize(S serialize, T const &value) {
return writer::StringWriter::oneshot([&serialize, &value](writer::StringWriter &w) noexcept {
return std::invoke(serialize, value, w);
});
}
Literal::Literal(storage::identifier::NodeBackendHandle handle) noexcept : Node{handle} {
}
Literal::Literal() noexcept : Node{storage::identifier::NodeBackendHandle{}} {
}
Literal Literal::make_null() noexcept {
return Literal{};
}
Literal Literal::make_simple_unchecked(std::string_view lexical_form, bool needs_escape, storage::DynNodeStoragePtr node_storage) {
return Literal{storage::identifier::NodeBackendHandle{node_storage.find_or_make_id(storage::view::LexicalFormLiteralBackendView{
.datatype_id = storage::identifier::NodeBackendID::xsd_string_iri.first,
.lexical_form = lexical_form,
.language_tag = "",
.needs_escape = needs_escape}),
node_storage}};
}
Literal Literal::make_noninlined_typed_unchecked(std::string_view lexical_form, bool needs_escape, IRI const &datatype, storage::DynNodeStoragePtr node_storage) {
return Literal{storage::identifier::NodeBackendHandle{node_storage.find_or_make_id(storage::view::LexicalFormLiteralBackendView{
.datatype_id = datatype.to_node_storage(node_storage).backend_handle().id(),
.lexical_form = lexical_form,
.language_tag = "",
.needs_escape = needs_escape}),
node_storage}};
}
Literal Literal::make_noninlined_special_unchecked(std::any &&value, storage::identifier::LiteralType fixed_id, storage::DynNodeStoragePtr node_storage) {
return Literal{storage::identifier::NodeBackendHandle{node_storage.find_or_make_id(storage::view::ValueLiteralBackendView{
.datatype = fixed_id,
.value = std::move(value)}),
node_storage}};
}
Literal Literal::make_lang_tagged_unchecked(std::string_view lexical_form, bool needs_escape, std::string_view lang, storage::DynNodeStoragePtr node_storage) {
auto node_id = node_storage.find_or_make_id(storage::view::LexicalFormLiteralBackendView{
.datatype_id = storage::identifier::NodeBackendID::rdf_langstring_iri.first,
.lexical_form = lexical_form,
.language_tag = lang,
.needs_escape = needs_escape});
return make_lang_tagged_unchecked_from_node_id(lang, node_storage, node_id);
}
Literal Literal::make_lang_tagged_unchecked_from_node_id(std::string_view lang, storage::DynNodeStoragePtr node_storage, storage::identifier::NodeBackendID node_id) noexcept {
using namespace storage::identifier;
auto const lang_tag_i = datatypes::registry::DatatypeRegistry::LangTagInlines::try_tag_to_inlined(lang); // check if the lang_tag can be inlined
if (!lang_tag_i.has_value()) {
return Literal{NodeBackendHandle{node_id, node_storage}};
}
auto const inlined_id = datatypes::registry::DatatypeRegistry::LangTagInlines::try_into_inlined(node_id.node_id().literal_id(), *lang_tag_i); // check if we have enough space#
if (!inlined_id.has_value()) {
return Literal{NodeBackendHandle{node_id, node_storage}};
}
return Literal{NodeBackendHandle{NodeBackendID{NodeID{*inlined_id, node_id.node_id().literal_type()},
RDFNodeType::Literal,
true,
node_id.free_tagging_bits()},
node_storage}};
}
Literal Literal::make_inlined_typed_unchecked(storage::identifier::LiteralID inlined_value, storage::identifier::LiteralType fixed_id, storage::DynNodeStoragePtr node_storage) noexcept {
using namespace storage::identifier;
RDF4CPP_ASSERT(fixed_id != LiteralType::other());
return Literal{NodeBackendHandle{NodeID{inlined_value, fixed_id},
RDFNodeType::Literal,
node_storage,
true}};
}
Literal Literal::make_typed_unchecked(std::any &&value, datatypes::registry::DatatypeIDView datatype, datatypes::registry::DatatypeRegistry::DatatypeEntry const &entry, storage::DynNodeStoragePtr node_storage) {
if (entry.inlining_ops.has_value()) {
if (auto const maybe_inlined = entry.inlining_ops->try_into_inlined_fptr(value); maybe_inlined.has_value()) {
return Literal::make_inlined_typed_unchecked(*maybe_inlined, datatype.get_fixed(), node_storage);
}
}
if (datatype.is_fixed()) {
if (auto const fixed_id = datatype.get_fixed(); node_storage.has_specialized_storage_for(fixed_id)) {
return Literal::make_noninlined_special_unchecked(std::move(value), fixed_id, node_storage);
}
}
auto const lex = run_serialize(entry.serialize_canonical_string_fptr, value);
return Literal::make_noninlined_typed_unchecked(lex,
false,
IRI{datatype, node_storage},
node_storage);
}
Literal Literal::make_string_like_copy_lang_tag(std::string_view str, Literal const &lang_tag_src, storage::DynNodeStoragePtr node_storage) {
auto const needs_escape = lexical_form_needs_escape(str);
if (lang_tag_src.datatype_eq<datatypes::rdf::LangString>()) {
return Literal::make_lang_tagged_unchecked(str, needs_escape, lang_tag_src.language_tag(), node_storage);
}
RDF4CPP_ASSERT(lang_tag_src.datatype_eq<datatypes::xsd::String>());
return Literal::make_simple_unchecked(str, needs_escape, node_storage);
}
Literal Literal::lang_tagged_get_de_inlined() const noexcept {
auto [_, id] = datatypes::registry::DatatypeRegistry::LangTagInlines::from_inlined(handle_.node_id().literal_id());
return Literal{storage::identifier::NodeBackendHandle{storage::identifier::NodeID{id, this->handle_.node_id().literal_type()},
handle_.type(),
handle_.storage(),
false,
handle_.free_tagging_bits()}};
}
bool Literal::dynamic_datatype_eq_impl(std::string_view datatype) const noexcept {
RDF4CPP_ASSERT(!this->is_fixed());
return this->datatype().identifier() == datatype;
}
Literal Literal::make_simple(std::string_view lexical_form, storage::DynNodeStoragePtr node_storage) {
if (!una::is_valid_utf8(lexical_form)) {
throw InvalidNode{"Invalid UTF-8 in lexical form of literal"};
}
auto const needs_escape = lexical_form_needs_escape(lexical_form);
return Literal::make_simple_unchecked(lexical_form, needs_escape, node_storage);
}
Literal Literal::make_simple_normalize(std::string_view lexical_form, storage::DynNodeStoragePtr node_storage) {
auto const lex = una::norm::to_nfc_utf8(lexical_form);
auto const needs_escape = lexical_form_needs_escape(lex);
return Literal::make_simple_unchecked(lex, needs_escape, node_storage);
}
Literal Literal::make_lang_tagged(std::string_view lexical_form, std::string_view lang_tag,
storage::DynNodeStoragePtr node_storage) {
if (!una::is_valid_utf8(lexical_form)) [[unlikely]] {
throw InvalidNode{"Invalid UTF-8 in lexical form of literal"};
}
auto const lowercase_lang_tag = una::cases::to_lowercase_utf8(lang_tag);
auto const needs_escape = lexical_form_needs_escape(lexical_form);
return Literal::make_lang_tagged_unchecked(lexical_form, needs_escape, lowercase_lang_tag, node_storage);
}
Literal Literal::make_lang_tagged_normalize(std::string_view lexical_form, std::string_view lang_tag,
storage::DynNodeStoragePtr node_storage) {
auto const lowercase_lang_tag = una::cases::to_lowercase_utf8(lang_tag);
auto const lex = una::norm::to_nfc_utf8(lexical_form);
auto const needs_escape = lexical_form_needs_escape(lex);
return Literal::make_lang_tagged_unchecked(lex, needs_escape, lowercase_lang_tag, node_storage);
}
Literal Literal::make_typed(std::string_view lexical_form, IRI const &datatype, storage::DynNodeStoragePtr node_storage) {
using namespace datatypes::registry;
DatatypeIDView const datatype_identifier{datatype};
if (datatype_identifier == datatypes::rdf::LangString::datatype_id) {
// see: https://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal
throw InvalidNode{"cannot construct rdf:langString without a language tag, please call one of the other factory functions"};
}
if (datatype_identifier == datatypes::xsd::String::datatype_id) {
return Literal::make_simple(lexical_form, node_storage);
}
if (auto const *entry = DatatypeRegistry::get_entry(datatype_identifier); entry != nullptr) {
// exists => canonize
auto cpp_value = entry->factory_fptr(lexical_form);
return Literal::make_typed_unchecked(std::move(cpp_value), datatype_identifier, *entry, node_storage);
} else {
// doesn't exist in the registry no way to canonicalize
auto const needs_escape = lexical_form_needs_escape(lexical_form);
return Literal::make_noninlined_typed_unchecked(lexical_form, needs_escape, datatype, node_storage);
}
}
Literal Literal::make_boolean(TriBool const b, storage::DynNodeStoragePtr node_storage) noexcept {
if (b == TriBool::Err) {
return Literal{};
}
return Literal::make_typed_from_value<datatypes::xsd::Boolean>(b == TriBool::True, node_storage);
}
Literal Literal::make_string_uuid(storage::DynNodeStoragePtr node_storage) {
boost::uuids::random_generator_mt19937 gen{};
boost::uuids::uuid u = gen();
std::string s = boost::uuids::to_string(u);
return make_simple(s, node_storage);
}
Literal Literal::generate_random_double(storage::DynNodeStoragePtr node_storage) {
static thread_local std::default_random_engine rng{std::random_device{}()};
return Literal::generate_random_double(rng, node_storage);
}
Literal Literal::to_node_storage(storage::DynNodeStoragePtr node_storage) const {
using datatypes::registry::DatatypeRegistry;
node_storage = select_node_storage(node_storage);
if (handle_.storage() == node_storage || null()) {
return *this;
}
if (this->is_inlined()) {
if (this->datatype_eq<datatypes::rdf::LangString>()) {
auto const data = this->lang_tagged_get_de_inlined().backend_handle().literal_backend().get_lexical();
return Literal::make_lang_tagged_unchecked(data.lexical_form,
data.needs_escape,
data.language_tag,
node_storage);
}
auto const node_id = this->handle_.node_id();
return Literal::make_inlined_typed_unchecked(node_id.literal_id(),
node_id.literal_type(),
node_storage);
}
auto literal_view = handle_.literal_backend();
auto node_id = literal_view.visit(
[&](storage::view::LexicalFormLiteralBackendView &lexical_backend) {
if (auto const dt_id = storage::identifier::iri_node_id_to_literal_type(lexical_backend.datatype_id);
dt_id.is_fixed() && node_storage.has_specialized_storage_for(dt_id)) {
// This node storage doesn't have specialized storage for the given type but the target node storage does.
// Need to send value over.
// This doesn't work for rdf:langString, but it shouldn't have a specialized storage anyway.
RDF4CPP_ASSERT(!this->datatype_eq<datatypes::rdf::LangString>());
auto const from_string = DatatypeRegistry::get_factory(dt_id);
auto value = from_string(lexical_backend.lexical_form);
return node_storage.find_or_make_id(storage::view::ValueLiteralBackendView{.datatype = dt_id,
.value = std::move(value)});
}
// send over IRI corresponding to this datatype
auto const dtype_iri_view = handle_.storage().find_iri_backend(lexical_backend.datatype_id);
lexical_backend.datatype_id = node_storage.find_or_make_id(dtype_iri_view);
// find or make the requested node
return node_storage.find_or_make_id(literal_view);
},
[&node_storage, &literal_view](storage::view::ValueLiteralBackendView const &value_backend) {
// no need to send over datatype IRI, as this having a specialized storage requires
// that the datatype is fixed, so it must already be present
if (!node_storage.has_specialized_storage_for(value_backend.datatype)) {
// target node storage is not specialized for this datatype, need to convert to lexical form
auto const serialize = DatatypeRegistry::get_serialize_canonical_string(datatypes::registry::DatatypeIDView{value_backend.datatype});
RDF4CPP_ASSERT(serialize != nullptr);
auto const lex = run_serialize(serialize, value_backend.value);
return node_storage.find_or_make_id(storage::view::LexicalFormLiteralBackendView{
.datatype_id = storage::identifier::literal_type_to_iri_node_id(value_backend.datatype),
.lexical_form = lex,
.language_tag = "",
.needs_escape = false});
}
// target node storage is also specialized for this datatype, directly send it over
return node_storage.find_or_make_id(literal_view);
});
return Literal{storage::identifier::NodeBackendHandle{node_id, node_storage}};
}
Literal Literal::try_get_in_node_storage(storage::DynNodeStoragePtr node_storage) const noexcept {
using datatypes::registry::DatatypeRegistry;
node_storage = select_node_storage(node_storage);
if (handle_.storage() == node_storage || null()) {
return *this;
}
if (this->is_inlined()) {
if (this->datatype_eq<datatypes::rdf::LangString>()) {
// special case for rdf:langString because part of it is in the backend
auto const literal_view = this->lang_tagged_get_de_inlined().handle_.literal_backend();
auto const tmp_id = node_storage.find_id(literal_view);
if (tmp_id.null()) {
return Literal{};
}
auto const lang_tag_i = DatatypeRegistry::LangTagInlines::try_tag_to_inlined(literal_view.get_lexical().language_tag);
if (!lang_tag_i.has_value()) {
return Literal{storage::identifier::NodeBackendHandle{tmp_id, node_storage}};
}
auto const inlined_id = DatatypeRegistry::LangTagInlines::try_into_inlined(tmp_id.node_id().literal_id(), *lang_tag_i);
if (!inlined_id.has_value()) {
return Literal{storage::identifier::NodeBackendHandle{tmp_id, node_storage}};
}
return Literal{storage::identifier::NodeBackendHandle{storage::identifier::NodeBackendID{storage::identifier::NodeID{*inlined_id, tmp_id.node_id().literal_type()},
storage::identifier::RDFNodeType::Literal,
true,
tmp_id.free_tagging_bits()},
node_storage}};
}
auto const node_id = this->handle_.node_id();
return Literal::make_inlined_typed_unchecked(node_id.literal_id(),
node_id.literal_type(),
node_storage);
}
auto literal_view = handle_.literal_backend();
auto const node_id = literal_view.visit(
[&](storage::view::LexicalFormLiteralBackendView &lexical_backend) noexcept {
if (auto const dt_id = storage::identifier::iri_node_id_to_literal_type(lexical_backend.datatype_id);
dt_id.is_fixed() && node_storage.has_specialized_storage_for(dt_id)) {
// This node storage doesn't have specialized storage for the given type but the target node storage does.
// Need to send value over.
// This doesn't work for rdf:langString, but it shouldn't have a specialized storage anyway.
RDF4CPP_ASSERT(!this->datatype_eq<datatypes::rdf::LangString>());
auto const from_string = DatatypeRegistry::get_factory(dt_id);
auto value = from_string(lexical_backend.lexical_form);
return node_storage.find_id(storage::view::ValueLiteralBackendView{.datatype = dt_id,
.value = std::move(value)});
}
// Default case.
// This node storage doesn't have specialized storage for the value and the new one also doesn't
auto src_node_storage = handle_.storage();
auto const dtype_iri_view = src_node_storage.find_iri_backend(lexical_backend.datatype_id);
lexical_backend.datatype_id = node_storage.find_id(dtype_iri_view);
if (lexical_backend.datatype_id.null()) {
// datatype IRI not present, therefore literal cannot be present
return storage::identifier::NodeBackendID{};
}
return node_storage.find_id(literal_view);
},
[&node_storage, &literal_view](storage::view::ValueLiteralBackendView const &value_backend) noexcept {
// no need to send over datatype IRI, as this having a specialized storage requires
// that the datatype is fixed, so it must already be present
if (!node_storage.has_specialized_storage_for(value_backend.datatype)) {
// target node storage is not specialized for this datatype, need to convert to lexical form
auto const serialize_canonical = DatatypeRegistry::get_serialize_canonical_string(datatypes::registry::DatatypeIDView{value_backend.datatype});
RDF4CPP_ASSERT(serialize_canonical != nullptr);
auto const lex = run_serialize(serialize_canonical, value_backend.value);
return node_storage.find_id(storage::view::LexicalFormLiteralBackendView{
.datatype_id = storage::identifier::literal_type_to_iri_node_id(value_backend.datatype),
.lexical_form = lex,
.language_tag = "",
.needs_escape = false});
}
// target node storage is also specialized for this datatype, directly try to get it
return node_storage.find_id(literal_view);
});
if (node_id.null()) {
return Literal{};
}
return Literal{storage::identifier::NodeBackendHandle{node_id, node_storage}};
}
storage::identifier::NodeBackendID Literal::find_datatype_iri(datatypes::registry::DatatypeIDView id, storage::DynNodeStoragePtr node_storage) noexcept {
auto nid = IRI::find(id, node_storage);
return nid.backend_handle().id();
}
Literal Literal::find_simple(std::string_view lexical_form, storage::DynNodeStoragePtr node_storage) noexcept {
auto esc = lexical_form_needs_escape(lexical_form);
auto nid = node_storage.find_id(storage::view::LexicalFormLiteralBackendView{
storage::identifier::NodeBackendID::xsd_string_iri.first, lexical_form, "", esc});
if (nid.null())
return Literal{};
return Literal{storage::identifier::NodeBackendHandle{nid, node_storage}};
}
Literal Literal::find_lang_tagged(std::string_view lexical_form, std::string_view lang_tag, storage::DynNodeStoragePtr node_storage) noexcept {
auto esc = lexical_form_needs_escape(lexical_form);
auto nid = node_storage.find_id(storage::view::LexicalFormLiteralBackendView{
storage::identifier::NodeBackendID::rdf_langstring_iri.first, lexical_form, lang_tag, esc});
if (nid.null())
return Literal{};
return make_lang_tagged_unchecked_from_node_id(lang_tag, node_storage, nid);
}
bool Literal::datatype_eq(IRI const &datatype) const noexcept {
return this->datatype_id() == datatypes::registry::DatatypeIDView{datatype};
}
bool Literal::datatype_eq(Literal const &other) const noexcept {
if (auto const this_type = this->handle_.node_id().literal_type(); this_type.is_fixed()) {
if (auto const other_type = other.handle_.node_id().literal_type(); other_type.is_fixed()) {
return this_type == other_type;
}
return false;
}
return this->datatype() == other.datatype();
}
Literal Literal::as_datatype_eq(IRI const &datatype, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
return Literal::make_boolean(this->datatype_eq(datatype), select_node_storage(node_storage));
}
Literal Literal::as_datatype_eq(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null() || other.null()) {
return Literal{};
}
return Literal::make_boolean(this->datatype_eq(other), select_node_storage(node_storage));
}
IRI Literal::datatype() const noexcept {
if (null()) {
return IRI::make_null();
}
if (this->is_fixed()) {
return IRI{storage::identifier::datatype_iri_handle_for_fixed_lit_handle(handle_)};
}
auto const lexical = handle_.literal_backend().get_lexical();
return IRI{storage::identifier::NodeBackendHandle{lexical.datatype_id,
handle_.storage()}};
}
template<bool simplified, typename C>
auto Literal::serialize_lexical_form_impl(C &&consume) const noexcept {
if (this->is_inlined()) {
if (this->datatype_eq<datatypes::rdf::LangString>()) {
auto const lex = this->lang_tagged_get_de_inlined()
.handle_
.literal_backend()
.get_lexical()
.lexical_form;
return std::invoke(std::forward<C>(consume), lex);
}
auto const *entry = datatypes::registry::DatatypeRegistry::get_entry(this->datatype_id());
RDF4CPP_ASSERT(entry != nullptr);
RDF4CPP_ASSERT(entry->inlining_ops.has_value());
auto const inlined_value = this->handle_.node_id().literal_id();
auto const value = entry->inlining_ops->from_inlined_fptr(inlined_value);
if constexpr (simplified) {
return std::invoke(std::forward<C>(consume), value, entry->serialize_simplified_string_fptr);
} else {
return std::invoke(std::forward<C>(consume), value, entry->serialize_canonical_string_fptr);
}
}
return handle_.literal_backend().visit(
[this, &consume](storage::view::LexicalFormLiteralBackendView const &lexical_backend) noexcept {
if constexpr (simplified) {
auto const *entry = datatypes::registry::DatatypeRegistry::get_entry(this->datatype_id());
if (entry != nullptr) {
auto const value = entry->factory_fptr(lexical_backend.lexical_form);
return std::invoke(std::forward<C>(consume), value, entry->serialize_simplified_string_fptr);
}
} else {
(void) this; // silence unused capture warning
}
return std::invoke(std::forward<C>(consume), lexical_backend.lexical_form);
},
[&consume](storage::view::ValueLiteralBackendView const &value_backend) noexcept {
if constexpr (simplified) {
auto const serialize = datatypes::registry::DatatypeRegistry::get_serialize_simplified_string(value_backend.datatype);
return std::invoke(std::forward<C>(consume), value_backend.value, serialize);
} else {
auto const serialize = datatypes::registry::DatatypeRegistry::get_serialize_canonical_string(value_backend.datatype);
return std::invoke(std::forward<C>(consume), value_backend.value, serialize);
}
});
}
// Using structs to ensure static-dispatch
struct ConsumeSafe {
[[nodiscard]] CowString operator()(std::string_view lexical) const noexcept {
return CowString{CowString::borrowed, lexical};
}
[[nodiscard]] CowString operator()(std::any const &value, datatypes::registry::DatatypeRegistry::serialize_fptr_t serialize) const noexcept {
auto s = writer::StringWriter::oneshot([&value, serialize](writer::StringWriter &w) noexcept {
return serialize(value, w);
});
return CowString{CowString::owned, std::move(s)};
}
};
struct ConsumeMaybeSerialize {
std::string_view *const out_lex_form;
writer::BufWriterParts const writer;
[[nodiscard]] FetchOrSerializeResult operator()(std::string_view const lexical) const noexcept {
*out_lex_form = lexical;
return FetchOrSerializeResult::Fetched;
}
[[nodiscard]] FetchOrSerializeResult operator()(std::any const &value, datatypes::registry::DatatypeRegistry::serialize_fptr_t const serialize) const noexcept {
if (!serialize(value, writer)) {
return FetchOrSerializeResult::SerializationFailed;
}
return FetchOrSerializeResult::Serialized;
}
};
struct ConsumeSerialize {
writer::BufWriterParts const writer;
bool operator()(std::string_view const lexical_form) const noexcept {
return writer::write_str(lexical_form, writer);
}
bool operator()(std::any const &value, datatypes::registry::DatatypeRegistry::serialize_fptr_t const serialize) const noexcept {
return serialize(value, writer);
}
};
CowString Literal::lexical_form() const noexcept {
return serialize_lexical_form_impl<false>(ConsumeSafe{});
}
FetchOrSerializeResult Literal::fetch_or_serialize_lexical_form(std::string_view &out_lex_form, writer::BufWriterParts const writer) const noexcept {
return serialize_lexical_form_impl<false>(ConsumeMaybeSerialize{&out_lex_form, writer});
}
Literal Literal::as_lexical_form(storage::DynNodeStoragePtr node_storage) const {
if (this->null()) {
return Literal{};
}
auto const lex = this->lexical_form();
auto const needs_escape = lexical_form_needs_escape(lex);
return Literal::make_simple_unchecked(lex, needs_escape, select_node_storage(node_storage));
}
CowString Literal::simplified_lexical_form() const noexcept {
return serialize_lexical_form_impl<true>(ConsumeSafe{});
}
FetchOrSerializeResult Literal::fetch_or_serialize_simplified_lexical_form(std::string_view &out_lex_form, writer::BufWriterParts const writer) const noexcept {
return serialize_lexical_form_impl<true>(ConsumeMaybeSerialize{&out_lex_form, writer});
}
Literal Literal::as_simplified_lexical_form(storage::DynNodeStoragePtr node_storage) const {
if (this->null()) {
return Literal{};
}
auto const lex = this->simplified_lexical_form();
auto const needs_escape = lexical_form_needs_escape(lex);
return Literal::make_simple_unchecked(lex, needs_escape, select_node_storage(node_storage));
}
std::string_view Literal::language_tag() const noexcept {
if (this->datatype_eq<datatypes::rdf::LangString>()) {
if (this->is_inlined()) {
auto [tag, _] = rdf4cpp::datatypes::registry::DatatypeRegistry::LangTagInlines::from_inlined(this->handle_.node_id().literal_id());
return rdf4cpp::datatypes::registry::DatatypeRegistry::LangTagInlines::inlined_to_tag(tag);
}
return handle_.literal_backend().get_lexical().language_tag;
}
return "";
}
Literal Literal::as_language_tag(storage::DynNodeStoragePtr node_storage) const {
if (this->null()) {
return Literal{};
}
return Literal::make_simple_unchecked(this->language_tag(), false, select_node_storage(node_storage));
}
TriBool Literal::language_tag_eq(std::string_view const lang_tag) const noexcept {
if (!this->datatype_eq<datatypes::rdf::LangString>()) {
return TriBool::Err;
}
return this->language_tag() == lang_tag;
}
TriBool Literal::language_tag_eq(Literal const &other) const noexcept {
if (!this->datatype_eq<datatypes::rdf::LangString>() || !other.datatype_eq<datatypes::rdf::LangString>()) {
return TriBool::Err;
}
return this->language_tag() == other.language_tag();
}
Literal Literal::as_language_tag_eq(std::string_view const lang_tag, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
return Literal::make_boolean(this->language_tag_eq(lang_tag), select_node_storage(node_storage));
}
Literal Literal::as_language_tag_eq(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null() || other.null()) {
return Literal{};
}
return Literal::make_boolean(this->language_tag_eq(other), select_node_storage(node_storage));
}
// https://www.w3.org/TR/n-triples/#grammar-production-STRING_LITERAL_QUOTE
#define RDF4CPP_DETAIL_TRY_SER_QUOTED_LEXICAL(lexical) \
RDF4CPP_DETAIL_TRY_WRITE_STR("\""); \
for (char const ch : lexical) { \
switch (ch) { \
case '"': { \
RDF4CPP_DETAIL_TRY_WRITE_STR(R"(\")"); \
break; \
} \
case '\\': { \
RDF4CPP_DETAIL_TRY_WRITE_STR(R"(\\)"); \
break; \
} \
case '\n': { \
RDF4CPP_DETAIL_TRY_WRITE_STR(R"(\n)"); \
break; \
} \
case '\r': { \
RDF4CPP_DETAIL_TRY_WRITE_STR(R"(\r)"); \
break; \
} \
[[likely]] default: { \
RDF4CPP_DETAIL_TRY_WRITE_STR(&ch, 1); \
break; \
} \
} \
} \
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
bool Literal::serialize(writer::BufWriterParts const writer, NodeSerializationOpts opts) const noexcept {
if (this->null()) {
RDF4CPP_DETAIL_TRY_WRITE_STR("null");
return true;
}
if (this->datatype_eq<datatypes::xsd::String>()) {
auto const value = this->backend_handle().literal_backend().get_lexical();
if (value.needs_escape) [[unlikely]] {
RDF4CPP_DETAIL_TRY_SER_QUOTED_LEXICAL(value.lexical_form);
} else {
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
RDF4CPP_DETAIL_TRY_WRITE_STR(value.lexical_form);
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
}
return true;
} else if (this->datatype_eq<datatypes::rdf::LangString>()) {
auto const value = this->lang_tagged_get_de_inlined().backend_handle().literal_backend().get_lexical();
if (value.needs_escape) [[unlikely]] {
RDF4CPP_DETAIL_TRY_SER_QUOTED_LEXICAL(value.lexical_form);
} else {
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
RDF4CPP_DETAIL_TRY_WRITE_STR(value.lexical_form);
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
}
RDF4CPP_DETAIL_TRY_WRITE_STR("@");
RDF4CPP_DETAIL_TRY_WRITE_STR(value.language_tag);
return true;
} else if (opts.use_short_form && (this->datatype_eq<datatypes::xsd::Integer>()
|| this->datatype_eq<datatypes::xsd::Decimal>()
|| this->datatype_eq<datatypes::xsd::Boolean>()
|| (this->datatype_eq<datatypes::xsd::Double>() && std::isfinite(this->value<datatypes::xsd::Double>())))) {
return this->serialize_lexical_form(writer);
} else if (this->is_inlined()) {
RDF4CPP_ASSERT(!this->datatype_eq<datatypes::rdf::LangString>());
// Notes:
// 1. inlined values are assumed to not require escaping
// 2. This is a known datatype because it is inlined => the registry contains the datatype IRI
auto const *entry = datatypes::registry::DatatypeRegistry::get_entry(this->datatype_id());
RDF4CPP_ASSERT(entry != nullptr);
RDF4CPP_ASSERT(entry->inlining_ops.has_value());
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
auto const inlined_value = this->backend_handle().node_id().literal_id();
if (!entry->serialize_canonical_string_fptr(entry->inlining_ops->from_inlined_fptr(inlined_value), writer)) {
return false;
}
RDF4CPP_DETAIL_TRY_WRITE_STR("\"^^");
return writer::write_fixed_type_iri(this->backend_handle().node_id().literal_type(), writer, opts.use_prefixes);
} else {
using storage::NodeStorage;
using storage::identifier::NodeBackendHandle;
return this->backend_handle().literal_backend().visit(
[&](storage::view::LexicalFormLiteralBackendView const &lexical_backend) noexcept {
auto const dtype_iri = handle_.storage().find_iri_backend(lexical_backend.datatype_id);
if (lexical_backend.needs_escape) [[unlikely]] {
RDF4CPP_DETAIL_TRY_SER_QUOTED_LEXICAL(lexical_backend.lexical_form);
} else {
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
RDF4CPP_DETAIL_TRY_WRITE_STR(lexical_backend.lexical_form);
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
}
RDF4CPP_DETAIL_TRY_WRITE_STR("^^<");
RDF4CPP_DETAIL_TRY_WRITE_STR(dtype_iri.identifier);
RDF4CPP_DETAIL_TRY_WRITE_STR(">");
return true;
},
[&](storage::view::ValueLiteralBackendView const &value_backend) noexcept {
// Notes:
// 1. non-string storage values are assumed to not require escaping
// 2. This is a known datatype because it is stored in a value backend => the registry contains the datatype IRI
auto const *entry = datatypes::registry::DatatypeRegistry::get_entry(this->datatype_id());
RDF4CPP_ASSERT(entry != nullptr);
RDF4CPP_DETAIL_TRY_WRITE_STR("\"");
if (!entry->serialize_canonical_string_fptr(value_backend.value, writer)) {
return false;
}
RDF4CPP_DETAIL_TRY_WRITE_STR("\"^^");
return writer::write_fixed_type_iri(this->backend_handle().node_id().literal_type(), writer, opts.use_prefixes);
});
}
}
#undef RDF4CPP_DETAIL_TRY_SER_QUOTED_LEXICAL
bool Literal::serialize_lexical_form(writer::BufWriterParts const writer) const noexcept {
return serialize_lexical_form_impl<false>(ConsumeSerialize{writer});
}
bool Literal::serialize_simplified_lexical_form(writer::BufWriterParts const writer) const noexcept {
return serialize_lexical_form_impl<true>(ConsumeSerialize{writer});
}
Literal::operator std::string() const noexcept {
return writer::StringWriter::oneshot([this](auto &w) noexcept {
return this->serialize(w);
});
}
bool Literal::is_numeric() const noexcept {
if (auto const is_num = this->handle_.node_id().literal_type().is_numeric(); is_num != TriBool::Err) {
return is_num;
}
return !this->null() && datatypes::registry::DatatypeRegistry::get_numerical_ops(this->datatype_id()) != nullptr;
}
bool Literal::is_timepoint() const noexcept {
if (auto const is_tp = this->handle_.node_id().literal_type().is_timepoint(); is_tp != TriBool::Err) {
return is_tp;
}
return !this->null() && datatypes::registry::DatatypeRegistry::get_timepoint_ops(this->datatype_id()) == nullptr;
}
bool Literal::is_duration() const noexcept {
if (auto const is_dur = this->handle_.node_id().literal_type().is_duration(); is_dur != TriBool::Err) {
return is_dur;
}
return !this->null() && datatypes::registry::DatatypeRegistry::get_duration_ops(this->datatype_id()) == nullptr;
}
std::ostream &operator<<(std::ostream &os, Literal const &literal) {
writer::BufOStreamWriter w{os};
literal.serialize(w);
w.finalize();
return os;
}
std::any Literal::value() const noexcept {
using namespace datatypes;
auto const datatype = this->datatype_id();
if (this->is_inlined()) {
if (datatype == rdf::LangString::datatype_id) {
return this->lang_tagged_get_de_inlined().value();
}
auto const ops = registry::DatatypeRegistry::get_inlining_ops(datatype);
RDF4CPP_ASSERT(ops != nullptr);
auto const inlined_value = this->handle_.node_id().literal_id();
return ops->from_inlined_fptr(inlined_value);
}
auto const backend = handle_.literal_backend();
if (datatype == rdf::LangString::datatype_id) {
auto const &lex = backend.get_lexical();
return std::any{registry::LangStringRepr{
.lexical_form = lex.lexical_form,
.language_tag = lex.language_tag}};
}
if (datatype == xsd::String::datatype_id) {
auto const &lex = backend.get_lexical();
return std::any{lex.lexical_form};
}
return backend.visit(
[&datatype](storage::view::LexicalFormLiteralBackendView const &lexical_backend) noexcept {
if (auto const factory = registry::DatatypeRegistry::get_factory(datatype); factory != nullptr) {
return factory(lexical_backend.lexical_form);
}
return std::any{};
},
[&datatype](storage::view::ValueLiteralBackendView const &value_backend) noexcept {
RDF4CPP_ASSERT(value_backend.datatype == datatype);
(void)datatype;
return value_backend.value;
});
}
Literal Literal::cast_impl(datatypes::registry::DatatypeIDView target_dtid, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
using namespace datatypes::xsd;
if (this->null()) {
return Literal{};
}
node_storage = select_node_storage(node_storage);
auto const this_dtid = this->datatype_id();
if (this_dtid == target_dtid) {
return this->to_node_storage(node_storage);
}
if (this_dtid == String::datatype_id) {
// string -> any
try {
auto const lex = this->lexical_form();
return Literal::make_typed(lex, IRI{target_dtid, node_storage}, node_storage);
} catch (...) {
return Literal{};
}
}
if (target_dtid == String::datatype_id) {
// any -> string
return this->as_simplified_lexical_form(node_storage);
}
if (target_dtid == Boolean::datatype_id) {
// any -> bool
return this->as_ebv(node_storage);
}
auto const *target_e = DatatypeRegistry::get_entry(target_dtid);
if (target_e == nullptr) {
// target not registered
return Literal{};
}
if (this_dtid == Boolean::datatype_id && target_e->numeric_ops.has_value()) {
// bool -> numeric
if (target_e->numeric_ops->is_impl()) {
auto value = this->template value<Boolean>() ? target_e->numeric_ops->get_impl().one_value_fptr()
: target_e->numeric_ops->get_impl().zero_value_fptr();
return Literal::make_typed_unchecked(std::move(value), target_dtid, *target_e, node_storage);
} else {
auto const &impl_converter = DatatypeRegistry::get_numeric_op_impl_conversion(*target_e);
auto const *target_num_impl = DatatypeRegistry::get_numerical_ops(impl_converter.target_type_id);
RDF4CPP_ASSERT(target_num_impl != nullptr);
// perform conversion as impl numeric type
auto const value = this->template value<Boolean>() ? target_num_impl->get_impl().one_value_fptr()
: target_num_impl->get_impl().zero_value_fptr();
// downcast to target
auto target_value = impl_converter.inverted_convert(value);
if (!target_value.has_value()) {
// not representable as target type
return Literal{};
}
return Literal::make_typed_unchecked(std::move(*target_value), target_dtid, *target_e, node_storage);
}
}
auto const *this_e = DatatypeRegistry::get_entry(this_dtid);
if (this_e == nullptr) {
// this datatype not registered
return Literal{};
}
if (auto const common_conversion = DatatypeRegistry::get_common_type_conversion(this_e->conversion_table, target_e->conversion_table); common_conversion.has_value()) {
// general cast
// TODO: if performance is bad split into separate cases for up-, down- and cross-casting to avoid one set of std::any wrapping and unwrapping for the former 2
auto const common_type_value = common_conversion->convert_lhs(this->value()); // upcast to common
auto target_value = common_conversion->inverted_convert_rhs(common_type_value); // downcast to target
if (!target_value.has_value()) {
// downcast failed
return Literal{};
}
return Literal::make_typed_unchecked(std::move(*target_value), target_dtid, *target_e, node_storage);
}
// no conversion found
return Literal{};
}
Literal Literal::cast(IRI const &target, storage::DynNodeStoragePtr node_storage) const {
return this->cast_impl(static_cast<datatypes::registry::DatatypeIDView>(target), node_storage);
}
template<typename OpSelect>
requires std::is_nothrow_invocable_r_v<datatypes::registry::DatatypeRegistry::binop_fptr_t, OpSelect, datatypes::registry::DatatypeRegistry::NumericOpsImpl const &>
Literal Literal::numeric_binop_impl(OpSelect op_select, Literal const &other, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
RDF4CPP_ASSERT(!this->null() && !other.null());
if (this->is_fixed_not_numeric() || other.is_fixed_not_numeric()) {
return Literal{};
}
auto const this_datatype = this->datatype_id();
auto const *this_entry = DatatypeRegistry::get_entry(this_datatype);
if (this_entry == nullptr || !this_entry->numeric_ops.has_value()) {
return Literal{}; // not registered or not numeric
}
auto const other_datatype = other.datatype_id();
if (this_datatype == other_datatype && this_entry->numeric_ops->is_impl()) {
DatatypeRegistry::OpResult op_res = op_select(this_entry->numeric_ops->get_impl())(this->value(),
other.value());
if (!op_res.result_value.has_value()) {
return Literal{};
}
auto const *result_entry = [&]() {
if (op_res.result_type_id == this_datatype) [[likely]] {
return this_entry;
} else [[unlikely]] {
return DatatypeRegistry::get_entry(op_res.result_type_id);
}
}();
RDF4CPP_ASSERT(result_entry != nullptr);
return Literal::make_typed_unchecked(std::move(*op_res.result_value), op_res.result_type_id, *result_entry, node_storage);
} else {
auto const *other_entry = DatatypeRegistry::get_entry(other_datatype);
if (other_entry == nullptr || !other_entry->numeric_ops.has_value()) {
return Literal{}; // not registered, or not numeric
}
auto const equalizer = DatatypeRegistry::get_common_numeric_op_type_conversion(*this_entry,
*other_entry);
if (!equalizer.has_value()) {
return Literal{}; // not convertible
}
auto const [equalized_entry, equalized_id] = [&]() {
if (equalizer->target_type_id == this_datatype) {
return std::make_pair(this_entry, this_datatype);
} else if (equalizer->target_type_id == other_datatype) {
return std::make_pair(other_entry, other_datatype);
} else {
return std::make_pair(DatatypeRegistry::get_entry(equalizer->target_type_id), equalizer->target_type_id);
}
}();
RDF4CPP_ASSERT(equalized_entry != nullptr);
RDF4CPP_ASSERT(equalized_entry->numeric_ops.has_value());
RDF4CPP_ASSERT(equalized_entry->numeric_ops->is_impl());
DatatypeRegistry::OpResult op_res = op_select(equalized_entry->numeric_ops->get_impl())(equalizer->convert_lhs(this->value()),
equalizer->convert_rhs(other.value()));
if (!op_res.result_value.has_value()) {
return Literal{};
}
auto const *result_entry = [&, equalized_id = std::ref(equalized_id), equalized_entry = equalized_entry]() {
if (op_res.result_type_id == equalized_id.get()) [[likely]] {
return equalized_entry;
} else [[unlikely]] {
return DatatypeRegistry::get_entry(op_res.result_type_id);
}
}();
RDF4CPP_ASSERT(result_entry != nullptr);
return Literal::make_typed_unchecked(std::move(*op_res.result_value), op_res.result_type_id, *result_entry, node_storage);
}
}
template<typename OpSelect>
requires std::is_nothrow_invocable_r_v<datatypes::registry::DatatypeRegistry::unop_fptr_t, OpSelect, datatypes::registry::DatatypeRegistry::NumericOpsImpl const &>
Literal Literal::numeric_unop_impl(OpSelect op_select, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
if (this->null() || this->is_fixed_not_numeric()) {
return Literal{};
}
node_storage = select_node_storage(node_storage);
auto const this_datatype = this->datatype_id();
auto const this_entry = DatatypeRegistry::get_entry(this_datatype);
if (this_entry == nullptr || !this_entry->numeric_ops.has_value()) {
return Literal{}; // this_datatype not numeric
}
auto const [operand_entry, value] = [&]() noexcept {
if (this_entry->numeric_ops->is_stub()) {
auto const &impl_converter = DatatypeRegistry::get_numeric_op_impl_conversion(*this_entry);
auto const target_num_ops = DatatypeRegistry::get_entry(impl_converter.target_type_id);
return std::make_pair(target_num_ops, impl_converter.convert(this->value()));
} else {
return std::make_pair(this_entry, this->value());
}
}();
RDF4CPP_ASSERT(operand_entry != nullptr);
RDF4CPP_ASSERT(operand_entry->numeric_ops.has_value());
RDF4CPP_ASSERT(operand_entry->numeric_ops->is_impl());
DatatypeRegistry::OpResult op_res = op_select(operand_entry->numeric_ops->get_impl())(value);
auto const *result_entry = [&op_res, operand_entry = operand_entry]() {
if (op_res.result_type_id == DatatypeIDView{operand_entry->datatype_iri}) [[likely]] {
return operand_entry;
} else [[unlikely]] {
return DatatypeRegistry::get_entry(op_res.result_type_id);
}
}();
RDF4CPP_ASSERT(result_entry != nullptr);
return Literal::make_typed_unchecked(std::move(*op_res.result_value), op_res.result_type_id, *result_entry, node_storage);
}
std::partial_ordering Literal::compare_impl(Literal const &other, std::strong_ordering *out_alternative_ordering) const noexcept {
using datatypes::registry::DatatypeRegistry;
if (this->handle_.null() || other.handle_.null()) {
if (out_alternative_ordering != nullptr) {
// ordering extensions (for e.g. ORDER BY) require that null nodes
// are always the smallest node
if (this->handle_.null() && other.handle_.null()) {
*out_alternative_ordering = std::strong_ordering::equivalent;
} else {
*out_alternative_ordering = this->handle_.null()
? std::strong_ordering::less
: std::strong_ordering::greater;
}
}
// "Apart from BOUND, COALESCE, NOT EXISTS and EXISTS, all functions and operators operate on RDF Terms and will produce a type error if any arguments are unbound."
// - https://www.w3.org/TR/sparql11-query/#evaluation
return std::partial_ordering::unordered;
}
if (this->handle_.id().node_id().literal_type().is_fixed() && this->handle_ == other.handle_) {
// Without the check for is_fixed() we would allow all datatypes (even unknown/uncomparable) ones to be compared using equality.
// This check however is somewhat of a workaround; it just happens to work correctly because all known (fixed) types happen to be comparable.
// If one of our datatypes was not comparable, it would fail in the following scenario:
// "abc"^^custom = "abc"^^custom => true
// "abc"^^custom != "abc"^^custom => false
// "abc"^^custom = "cde"^^custom => null
// "abc"^^custom != "cde"^^custom => null
// As you can see the behaviour would be inconsistent.
// TODO we should probably fix this properly if we ever add a non-comparable type
return std::partial_ordering::equivalent;
}
auto const this_datatype = this->datatype_id();
auto const other_datatype = other.datatype_id();
std::strong_ordering const datatype_cmp_res = this_datatype <=> other_datatype;
auto const this_entry = DatatypeRegistry::get_entry(this_datatype);
if (datatype_cmp_res == std::strong_ordering::equal) {
if (out_alternative_ordering != nullptr) {
// types equal, fallback to lexical form ordering
*out_alternative_ordering = this->lexical_form() <=> other.lexical_form();
}
if (this_entry == nullptr || this_entry->compare_fptr == nullptr) {
return std::partial_ordering::unordered;
}
return this_entry->compare_fptr(this->value(), other.value());
} else {
if (out_alternative_ordering != nullptr) {
// types are different, the only useful alternative ordering is the type ordering
*out_alternative_ordering = datatype_cmp_res;
}
auto const other_entry = DatatypeRegistry::get_entry(other_datatype);
if (this_entry == nullptr || this_entry->compare_fptr == nullptr || other_entry == nullptr || other_entry->compare_fptr == nullptr) {
return std::partial_ordering::unordered;
}
auto const equalizer = DatatypeRegistry::get_common_type_conversion(this_entry->conversion_table,
other_entry->conversion_table);
if (!equalizer.has_value()) {
return std::partial_ordering::unordered; // not convertible to common type
}
auto const equalized_compare_fptr = [&]() {
if (equalizer->target_type_id == this_datatype) {
return this_entry->compare_fptr;
} else if (equalizer->target_type_id == other_datatype) {
return other_entry->compare_fptr;
} else {
return DatatypeRegistry::get_compare(equalizer->target_type_id);
}
}();
RDF4CPP_ASSERT(equalized_compare_fptr != nullptr);
return equalized_compare_fptr(equalizer->convert_lhs(this->value()),
equalizer->convert_rhs(other.value()));
}
}
std::partial_ordering Literal::compare(Literal const &other) const noexcept {
return this->compare_impl(other);
}
std::strong_ordering Literal::order(Literal const &other) const noexcept {
// default to equivalent; as required by compare_impl
// see doc for compare_impl
std::strong_ordering alternative_cmp_res = std::strong_ordering::equivalent;
auto const cmp_res = this->compare_impl(other, &alternative_cmp_res);
if (cmp_res == std::partial_ordering::equivalent || cmp_res == std::partial_ordering::unordered) {
// return alternative ordering instead
return alternative_cmp_res;
} else if (cmp_res == std::partial_ordering::less) {
return std::strong_ordering::less;
} else { // cmp_res == std::partial_ordering::greater
return std::strong_ordering::greater;
}
}
TriBool Literal::eq(Literal const &other) const noexcept {
return util::partial_weak_ordering_eq(this->compare(other), std::weak_ordering::equivalent);
}
Literal Literal::as_eq(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->eq(other), select_node_storage(node_storage));
}
TriBool Literal::ne(Literal const &other) const noexcept {
return !util::partial_weak_ordering_eq(this->compare(other), std::weak_ordering::equivalent);
}
Literal Literal::as_ne(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->ne(other), select_node_storage(node_storage));
}
TriBool Literal::lt(Literal const &other) const noexcept {
return util::partial_weak_ordering_eq(this->compare(other), std::weak_ordering::less);
}
Literal Literal::as_lt(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->lt(other), select_node_storage(node_storage));
}
TriBool Literal::le(Literal const &other) const noexcept {
return !util::partial_weak_ordering_eq(this->compare(other), std::weak_ordering::greater);
}
Literal Literal::as_le(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->le(other), select_node_storage(node_storage));
}
TriBool Literal::gt(Literal const &other) const noexcept {
return util::partial_weak_ordering_eq(this->compare(other), std::weak_ordering::greater);
}
Literal Literal::as_gt(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->gt(other), select_node_storage(node_storage));
}
TriBool Literal::ge(Literal const &other) const noexcept {
return !util::partial_weak_ordering_eq(this->compare(other), std::weak_ordering::less);
}
Literal Literal::as_ge(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->ge(other), select_node_storage(node_storage));
}
bool Literal::order_eq(Literal const &other) const noexcept {
return order(other) == std::weak_ordering::equivalent;
}
Literal Literal::as_order_eq(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->order_eq(other), select_node_storage(node_storage));
}
bool Literal::order_ne(Literal const &other) const noexcept {
return order(other) != std::weak_ordering::equivalent;
}
Literal Literal::as_order_ne(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->order_ne(other), select_node_storage(node_storage));
}
bool Literal::order_lt(Literal const &other) const noexcept {
return order(other) == std::weak_ordering::less;
}
Literal Literal::as_order_lt(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->order_lt(other), select_node_storage(node_storage));
}
bool Literal::order_le(Literal const &other) const noexcept {
return order(other) != std::weak_ordering::greater;
}
Literal Literal::as_order_le(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->order_le(other), select_node_storage(node_storage));
}
bool Literal::order_gt(Literal const &other) const noexcept {
return order(other) == std::weak_ordering::greater;
}
Literal Literal::as_order_gt(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->order_gt(other), select_node_storage(node_storage));
}
bool Literal::order_ge(Literal const &other) const noexcept {
return order(other) != std::weak_ordering::less;
}
Literal Literal::as_order_ge(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->order_ge(other), select_node_storage(node_storage));
}
std::partial_ordering Literal::operator<=>(Literal const &other) const noexcept {
return compare(other);
}
bool Literal::operator==(Literal const &other) const noexcept {
return this->eq(other);
}
datatypes::registry::DatatypeIDView Literal::datatype_id() const noexcept {
RDF4CPP_ASSERT(!this->null());
auto const lit_type = this->handle_.node_id().literal_type();
if (lit_type.is_fixed()) {
return datatypes::registry::DatatypeIDView{lit_type};
} else {
return datatypes::registry::DatatypeIDView{this->datatype().identifier()};
}
}
bool Literal::is_fixed() const noexcept {
RDF4CPP_ASSERT(!this->null());
return this->handle_.node_id().literal_type().is_fixed();
}
bool Literal::is_fixed_not_numeric() const noexcept {
return this->handle_.node_id().literal_type().is_numeric() == TriBool::False;
}
bool Literal::is_fixed_not_timepoint() const noexcept {
return this->handle_.node_id().literal_type().is_timepoint() == TriBool::False;
}
bool Literal::is_fixed_not_duration() const noexcept {
return this->handle_.node_id().literal_type().is_duration() == TriBool::False;
}
bool Literal::is_string_like() const noexcept {
auto const type = this->handle_.node_id().literal_type();
if (!type.is_fixed()) {
return false;
}
// TODO: this does not cover custom types deriving from xsd:string or xsd:langString
return type == datatypes::xsd::String::fixed_id || type == datatypes::rdf::LangString::fixed_id;
}
template<typename Op>
std::optional<Literal> Literal::run_binop(Literal const &other,
datatypes::registry::DatatypeIDView const &this_datatype,
datatypes::registry::DatatypeRegistry::DatatypeEntry const &this_entry,
datatypes::registry::DatatypeIDView const &other_datatype,
datatypes::registry::DatatypeRegistry::DatatypeEntry const &other_entry,
storage::DynNodeStoragePtr node_storage,
Op &&op) const {
using namespace datatypes::registry;
if (this_datatype == other_datatype) {
// fast path, equal types
DatatypeRegistry::OpResult op_res = std::invoke(std::forward<Op>(op),
this_entry,
this->value(),
other.value());
if (!op_res.result_value.has_value()) {
// operation failed
return Literal{};
}
auto const *result_entry = [&]() noexcept {
if (op_res.result_type_id == this_datatype) [[likely]] {
return &this_entry;
} else [[unlikely]] {
return DatatypeRegistry::get_entry(op_res.result_type_id);
}
}();
RDF4CPP_ASSERT(result_entry != nullptr);
return Literal::make_typed_unchecked(std::move(*op_res.result_value), op_res.result_type_id, *result_entry, node_storage);
} else {
// slow path, needs conversion
auto equalizer = DatatypeRegistry::get_common_type_conversion(this_entry.conversion_table, other_entry.conversion_table);
if (!equalizer.has_value()) {
return std::nullopt;
}
auto const [equalized_entry, equalized_id] = [&]() {
if (equalizer->target_type_id == this_datatype) {
return std::make_pair(&this_entry, this_datatype);
} else if (equalizer->target_type_id == other_datatype) {
return std::make_pair(&other_entry, other_datatype);
} else {
return std::make_pair(DatatypeRegistry::get_entry(equalizer->target_type_id), equalizer->target_type_id);
}
}();
RDF4CPP_ASSERT(equalized_entry != nullptr);
DatatypeRegistry::OpResult op_res = std::invoke(std::forward<Op>(op),
*equalized_entry,
equalizer->convert_lhs(this->value()),
equalizer->convert_rhs(other.value()));
if (!op_res.result_value.has_value()) {
// operation failed
return Literal{};
}
auto const *result_entry = [&, equalized_id = std::ref(equalized_id), equalized_entry = equalized_entry]() {
if (op_res.result_type_id == equalized_id.get()) [[likely]] {
return equalized_entry;
} else [[unlikely]] {
return DatatypeRegistry::get_entry(op_res.result_type_id);
}
}();
RDF4CPP_ASSERT(result_entry != nullptr);
return Literal::make_typed_unchecked(std::move(*op_res.result_value), op_res.result_type_id, *result_entry, node_storage);
}
}
template<typename Op>
std::optional<Literal> Literal::run_binop_cast_rhs(Literal const &other,
datatypes::registry::DatatypeRegistry::DatatypeEntry const &other_entry,
datatypes::registry::DatatypeIDView const &other_target,
storage::DynNodeStoragePtr node_storage,
Op &&op) const {
using namespace datatypes::registry;
RDF4CPP_ASSERT(!this->null() && !other.null());
auto const *target_entry = DatatypeRegistry::get_entry(other_target);
RDF4CPP_ASSERT(target_entry != nullptr);
auto const other_converter = DatatypeRegistry::get_common_type_conversion(other_entry.conversion_table, target_entry->conversion_table);
if (!other_converter.has_value()) {
return std::nullopt;
}
if (other_converter->target_type_id != other_target) {
// invalid cast
return std::nullopt;
}
auto const other_casted = other_converter->convert_lhs(other.value());
DatatypeRegistry::OpResult op_res = std::invoke(std::forward<Op>(op), this->value(), other_casted);
if (!op_res.result_value.has_value()) {
return Literal{};
}
auto const *result_entry = DatatypeRegistry::get_entry(op_res.result_type_id);
return Literal::make_typed_unchecked(std::move(*op_res.result_value), op_res.result_type_id, *result_entry, node_storage);
}
std::optional<Literal> Literal::chrono_add_impl(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
if ((this->is_fixed_not_timepoint() && this->is_fixed_not_duration()) || other.is_fixed_not_duration()) {
// not any of
// timepoint + duration
// duration + duration
return std::nullopt;
}
auto const this_datatype = this->datatype_id();
auto const other_datatype = other.datatype_id();
auto const *this_entry = DatatypeRegistry::get_entry(this_datatype);
if (this_entry == nullptr) {
return std::nullopt; // not registered
}
auto const *other_entry = DatatypeRegistry::get_entry(other_datatype);
if (other_entry == nullptr) {
return std::nullopt; // not registered
}
if (!other_entry->duration_ops.has_value()) {
// other is not duration
return std::nullopt;
}
if (this_entry->timepoint_ops.has_value()) {
return run_binop_cast_rhs(other, *other_entry, this_entry->timepoint_ops->timepoint_duration_type, node_storage,
[this_entry](std::any const &lhs, std::any const &rhs) noexcept {
return this_entry->timepoint_ops->timepoint_duration_add(lhs, rhs);
});
}
if (this_entry->duration_ops.has_value()) {
return run_binop(other, this_datatype, *this_entry, other_datatype, *other_entry, node_storage,
[](DatatypeRegistry::DatatypeEntry const &entry, std::any const &lhs, std::any const &rhs) noexcept {
RDF4CPP_ASSERT(entry.duration_ops.has_value());
return entry.duration_ops->duration_add(lhs, rhs);
});
}
return std::nullopt;
}
Literal Literal::add(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
if (this->null() || other.null()) {
return Literal{};
}
node_storage = select_node_storage(node_storage);
if (auto res = this->chrono_add_impl(other, node_storage); res.has_value()) {
return *res;
}
return this->numeric_binop_impl(
[](auto const &num_ops) noexcept {
return num_ops.add_fptr;
},
other, node_storage);
}
Literal Literal::operator+(Literal const &other) const {
return this->add(other);
}
Literal &Literal::add_assign(const Literal &other, storage::DynNodeStoragePtr node_storage) {
auto result = this->add(other, node_storage);
this->handle_ = result.handle_;
return *this;
}
Literal &Literal::operator+=(const Literal &other) {
return this->add_assign(other);
}
std::optional<Literal> Literal::chrono_sub_impl(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
RDF4CPP_ASSERT(!this->null() && !other.null());
if ((this->is_fixed_not_timepoint() && this->is_fixed_not_duration()) || (other.is_fixed_not_timepoint() && other.is_fixed_not_duration())) {
// is not any of
// duration - duration
// timepoint - timepoint
// timepoint - duration
return std::nullopt;
}
auto const this_datatype = this->datatype_id();
auto const other_datatype = other.datatype_id();
auto const *this_entry = DatatypeRegistry::get_entry(this_datatype);
if (this_entry == nullptr) {
return std::nullopt; // not registered
}
auto const *other_entry = DatatypeRegistry::get_entry(other_datatype);
if (other_entry == nullptr) {
return std::nullopt; // not registered
}
if (this_entry->timepoint_ops.has_value()) {
if (other_entry->timepoint_ops.has_value()) {
// timepoint - timepoint
return run_binop(other, this_datatype, *this_entry, other_datatype, *other_entry, node_storage,
[](DatatypeRegistry::DatatypeEntry const &entry, std::any const &lhs, std::any const &rhs) noexcept {
RDF4CPP_ASSERT(entry.timepoint_ops.has_value());
return entry.timepoint_ops->timepoint_sub(lhs, rhs);
});
}
if (other_entry->duration_ops.has_value()) {
// timepoint - duration
return run_binop_cast_rhs(other, *other_entry, this_entry->timepoint_ops->timepoint_duration_type, node_storage,
[this_entry](std::any const &lhs, std::any const &rhs) noexcept {
return this_entry->timepoint_ops->timepoint_duration_sub(lhs, rhs);
});
}
}
if (this_entry->duration_ops.has_value() && other_entry->duration_ops.has_value()) {
// duration - duration
return run_binop(other, this_datatype, *this_entry, other_datatype, *other_entry, node_storage,
[](DatatypeRegistry::DatatypeEntry const &entry, std::any const &lhs, std::any const &rhs) noexcept {
RDF4CPP_ASSERT(entry.duration_ops.has_value());
return entry.duration_ops->duration_sub(lhs, rhs);
});
}
return std::nullopt;
}
Literal Literal::sub(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
if (this->null() || other.null()) {
return Literal{};
}
node_storage = select_node_storage(node_storage);
if (auto res = chrono_sub_impl(other, node_storage); res.has_value()) {
return *res;
}
return this->numeric_binop_impl(
[](auto const &num_ops) noexcept {
return num_ops.sub_fptr;
},
other, node_storage);
}
Literal Literal::operator-(Literal const &other) const {
return this->sub(other);
}
Literal &Literal::sub_assign(const Literal &other, storage::DynNodeStoragePtr node_storage) {
auto result = this->sub(other, node_storage);
this->handle_ = result.handle_;
return *this;
}
Literal &Literal::operator-=(const Literal &other) {
return this->sub_assign(other);
}
std::optional<Literal> Literal::chrono_mul_impl(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
RDF4CPP_ASSERT(!this->null() && !other.null());
if (this->is_fixed_not_duration() && other.is_fixed_not_numeric()) {
// is not
// duration * scalar
return std::nullopt;
}
auto const this_datatype = this->datatype_id();
auto const *this_entry = DatatypeRegistry::get_entry(this_datatype);
if (this_entry == nullptr || !this_entry->duration_ops.has_value()) {
// lhs is not registered or not a duration
return std::nullopt;
}
auto const other_datatype = other.datatype_id();
auto const *other_entry = DatatypeRegistry::get_entry(other_datatype);
if (other_entry == nullptr || !other_entry->numeric_ops.has_value()) {
// rhs is not registered or not numeric
return std::nullopt;
}
// duration * scalar
return run_binop_cast_rhs(other, *other_entry, this_entry->duration_ops->duration_scalar_type, node_storage,
[this_entry](std::any const &lhs, std::any const &rhs) noexcept {
return this_entry->duration_ops->duration_scalar_mul(lhs, rhs);
});
}
Literal Literal::mul(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
if (this->null() || other.null()) {
return Literal{};
}
node_storage = select_node_storage(node_storage);
if (auto res = chrono_mul_impl(other, node_storage); res.has_value()) {
return *res;
}
return this->numeric_binop_impl(
[](auto const &num_ops) noexcept {
return num_ops.mul_fptr;
},
other, node_storage);
}
Literal Literal::operator*(Literal const &other) const {
return this->mul(other);
}
Literal &Literal::mul_assign(const Literal &other, storage::DynNodeStoragePtr node_storage) {
auto result = this->mul(other, node_storage);
this->handle_ = result.handle_;
return *this;
}
Literal &Literal::operator*=(const Literal &other) {
return this->mul_assign(other);
}
std::optional<Literal> Literal::chrono_div_impl(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes::registry;
RDF4CPP_ASSERT(!this->null() && !other.null());
if (this->is_fixed_not_duration() || (other.is_fixed_not_duration() && other.is_fixed_not_numeric())) {
// is not any of
// duration / duration
// duration / scalar
return std::nullopt;
}
auto const this_datatype = this->datatype_id();
auto const *this_entry = DatatypeRegistry::get_entry(this_datatype);
if (this_entry == nullptr || !this_entry->duration_ops.has_value()) {
// lhs is not registered or not duration
return std::nullopt;
}
auto const other_datatype = other.datatype_id();
auto const *other_entry = DatatypeRegistry::get_entry(other_datatype);
if (other_entry == nullptr) {
return std::nullopt; // other is not registered
}
if (other_entry->duration_ops.has_value()) {
// this & other are durations
auto const binop_res = run_binop(other, this_datatype, *this_entry, other_datatype, *other_entry, node_storage,
[](DatatypeRegistry::DatatypeEntry const &entry, std::any const &lhs, std::any const &rhs) noexcept {
RDF4CPP_ASSERT(entry.duration_ops.has_value());
return entry.duration_ops->duration_div(lhs, rhs);
});
if (binop_res.has_value()) {
return binop_res;
}
}
if (other_entry->numeric_ops.has_value()) {
// this is duration & other is scalar
return run_binop_cast_rhs(other, *other_entry, this_entry->duration_ops->duration_scalar_type, node_storage,
[this_entry](std::any const &lhs, std::any const &rhs) noexcept {
return this_entry->duration_ops->duration_scalar_div(lhs, rhs);
});
}
return std::nullopt;
}
Literal Literal::div(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
if (this->null() || other.null()) {
return Literal{};
}
node_storage = select_node_storage(node_storage);
if (auto res = chrono_div_impl(other, node_storage); res.has_value()) {
return *res;
}
return this->numeric_binop_impl(
[](auto const &num_ops) noexcept {
return num_ops.div_fptr;
},
other, node_storage);
}
Literal Literal::operator/(Literal const &other) const {
return this->div(other);
}
Literal &Literal::div_assign(const Literal &other, storage::DynNodeStoragePtr node_storage) {
auto result = this->div(other, node_storage);
this->handle_ = result.handle_;
return *this;
}
Literal &Literal::operator/=(const Literal &other) {
return this->div_assign(other);
}
Literal Literal::pos(storage::DynNodeStoragePtr node_storage) const {
return this->numeric_unop_impl(
[](auto const &num_ops) noexcept {
return num_ops.pos_fptr;
},
select_node_storage(node_storage));
}
Literal Literal::operator+() const {
return this->pos();
}
Literal Literal::neg(storage::DynNodeStoragePtr node_storage) const {
return this->numeric_unop_impl(
[](auto const &num_ops) noexcept {
return num_ops.neg_fptr;
},
select_node_storage(node_storage));
}
Literal Literal::operator-() const {
return this->neg();
}
Literal Literal::abs(storage::DynNodeStoragePtr node_storage) const {
return this->numeric_unop_impl(
[](auto const &num_ops) noexcept {
return num_ops.abs_fptr;
},
select_node_storage(node_storage));
}
Literal Literal::round(storage::DynNodeStoragePtr node_storage) const {
return this->numeric_unop_impl(
[](auto const &num_ops) noexcept {
return num_ops.round_fptr;
},
select_node_storage(node_storage));
}
Literal Literal::floor(storage::DynNodeStoragePtr node_storage) const {
return this->numeric_unop_impl(
[](auto const &num_ops) noexcept {
return num_ops.floor_fptr;
},
select_node_storage(node_storage));
}
Literal Literal::ceil(storage::DynNodeStoragePtr node_storage) const {
return this->numeric_unop_impl(
[](auto const &num_ops) noexcept {
return num_ops.ceil_fptr;
},
select_node_storage(node_storage));
}
TriBool Literal::ebv() const noexcept {
if (this->null()) {
return TriBool::Err;
}
auto const ebv = datatypes::registry::DatatypeRegistry::get_ebv(this->datatype_id());
if (ebv == nullptr) {
return TriBool::Err;
}
return ebv(this->value());
}
Literal Literal::as_ebv(storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->ebv(), select_node_storage(node_storage));
}
Literal Literal::logical_and(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->ebv() && other.ebv(), select_node_storage(node_storage));;
}
Literal Literal::operator&&(Literal const &other) const noexcept {
return this->logical_and(other);
}
Literal Literal::logical_or(Literal const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(this->ebv() || other.ebv(), select_node_storage(node_storage));
}
Literal Literal::operator||(Literal const &other) const noexcept {
return this->logical_or(other);
}
Literal Literal::logical_not(storage::DynNodeStoragePtr node_storage) const noexcept {
return Literal::make_boolean(!this->ebv(), select_node_storage(node_storage));
}
Literal Literal::operator!() const noexcept {
return this->logical_not();
}
std::optional<size_t> Literal::strlen() const noexcept {
if (!this->is_string_like()) {
return std::nullopt;
}
auto const lf = this->lexical_form();
auto u = lf | una::views::utf8;
return std::distance(u.begin(), u.end());
}
Literal Literal::as_strlen(storage::DynNodeStoragePtr node_storage) const {
if (this->null()) {
return Literal{};
}
auto const len = this->strlen();
if (!len.has_value()) {
return Literal{};
}
return Literal::make_typed_from_value<datatypes::xsd::Integer>(datatypes::xsd::Integer::cpp_type{*len}, select_node_storage(node_storage));
}
TriBool Literal::language_tag_matches_range(std::string_view const lang_range) const noexcept {
if (!this->is_string_like()) {
return TriBool::Err;
}
return lang_matches(this->language_tag(), lang_range);
}
Literal Literal::as_language_tag_matches_range(std::string_view lang_range, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
auto const res = this->language_tag_matches_range(lang_range);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::as_language_tag_matches_range(Literal const &lang_range, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
if (!lang_range.datatype_eq<datatypes::xsd::String>()) {
return Literal{};
}
auto const lang_range_lex = lang_range.lexical_form();
auto const res = this->language_tag_matches_range(lang_range_lex);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
static regex::Regex::flag_type translate_regex_flags(std::string_view const xpath_flags) {
return std::accumulate(xpath_flags.begin(), xpath_flags.end(), regex::RegexFlags::none(), [](auto facc, char c) {
switch (c) {
case 's':
return facc | regex::RegexFlag::DotAll;
case 'i':
return facc | regex::RegexFlag::CaseInsensitive;
case 'q':
return facc | regex::RegexFlag::Literal;
case 'm':
return facc | regex::RegexFlag::Multiline;
case 'x':
return facc | regex::RegexFlag::RemoveWhitespace;
default:
throw std::runtime_error{std::string{"Encountered unsupported regex flag: "} + c};
}
});
}
TriBool Literal::regex_matches(regex::Regex const &pattern) const noexcept {
if (!this->is_string_like()) {
return TriBool::Err;
}
auto const lex = this->lexical_form();
return pattern.regex_search(lex);
}
Literal Literal::as_regex_matches(regex::Regex const &pattern, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
auto const res = this->regex_matches(pattern);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::as_regex_matches(Literal const &pattern, Literal const &flags, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null() || !this->is_string_like()) {
return Literal{};
}
auto const re = [&]() noexcept -> std::optional<regex::Regex> {
try {
return pattern.make_regex(flags);
} catch (std::runtime_error const &) {
return std::nullopt;
}
}();
if (!re.has_value()) {
return Literal{};
}
auto const res = this->regex_matches(*re);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
regex::Regex Literal::make_regex(Literal const &flags) const {
if (this->null() || !this->datatype_eq<datatypes::xsd::String>()) {
throw std::runtime_error{"Literal cannot be converted for creating regex (null or not simple literal)"};
}
if (flags.null() || !flags.datatype_eq<datatypes::xsd::String>()) {
throw std::runtime_error{"Flags cannot be used for creating regex (null or not string-like)"};
}
auto const regex_flags = translate_regex_flags(flags.lexical_form());
return regex::Regex{this->lexical_form(), regex_flags};
}
Literal Literal::regex_replace(regex::RegexReplacer const &replacer, storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like()) {
return Literal{};
}
auto lf = this->lexical_form().into_owned();
try {
replacer.regex_replace(lf);
} catch (std::runtime_error const &) {
return Literal{};
}
return Literal::make_string_like_copy_lang_tag(lf, *this, select_node_storage(node_storage));
}
Literal Literal::regex_replace(Literal const &pattern, Literal const &replacement, Literal const &flags, storage::DynNodeStoragePtr node_storage) const {
if (this->null() || !this->is_string_like()) {
return Literal{};
}
try {
return this->regex_replace(pattern.make_regex(flags).make_replacer(replacement.lexical_form()), node_storage);
} catch (std::runtime_error const &) {
return Literal{};
}
}
TriBool Literal::contains(std::string_view const needle) const noexcept {
if (!this->is_string_like()) {
return TriBool::Err;
}
auto const s = this->lexical_form();
auto const r = una::casesens::find_utf8(s, needle);
return static_cast<bool>(r);
}
Literal Literal::as_contains(std::string_view const needle, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
auto const res = this->contains(needle);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::as_contains(Literal const &needle, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
if (needle.datatype_eq<datatypes::rdf::LangString>() && this->language_tag() != needle.language_tag()) {
return Literal{};
}
if (!needle.is_string_like())
return Literal::make_boolean(true, select_node_storage(node_storage));
auto const needle_lex = needle.lexical_form();
auto const res = this->contains(needle_lex);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::substr_before(std::string_view const needle, storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like()) {
return Literal{};
}
if (needle.empty()) {
return Literal::make_string_like_copy_lang_tag("", *this, select_node_storage(node_storage));
}
auto const s = this->lexical_form();
auto const r = una::casesens::find_utf8(s, needle);
if (!r) {
return Literal::make_simple_unchecked("", false, select_node_storage(node_storage));
}
auto substr = s.view().substr(0, r.pos()); // find_utf8 returns byte position, not unicode character position
return Literal::make_string_like_copy_lang_tag(substr, *this, select_node_storage(node_storage));
}
Literal Literal::substr_before(Literal const &needle, storage::DynNodeStoragePtr node_storage) const {
if (needle.datatype_eq<datatypes::rdf::LangString>() && this->language_tag() != needle.language_tag()) {
return Literal{};
}
if (!needle.is_string_like()) {
return Literal{};
}
auto const needle_lex = needle.lexical_form();
return this->substr_before(needle_lex, node_storage);
}
Literal Literal::substr_after(std::string_view const needle, storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like()) {
return Literal{};
}
if (needle.empty()) {
return *this;
}
auto const s = this->lexical_form();
auto const r = una::casesens::find_utf8(s, needle);
if (!r) {
return Literal::make_simple_unchecked("", false, select_node_storage(node_storage));
}
auto const substr = s.view().substr(r.pos() + needle.size()); // find_utf8 returns byte position, not unicode character position
return Literal::make_string_like_copy_lang_tag(substr, *this, select_node_storage(node_storage));
}
Literal Literal::substr_after(Literal const &needle, storage::DynNodeStoragePtr node_storage) const {
if (needle.datatype_eq<datatypes::rdf::LangString>() && this->language_tag() != needle.language_tag()) {
return Literal{};
}
if (!needle.is_string_like()) {
return Literal{};
}
return this->substr_after(needle.lexical_form(), node_storage);
}
TriBool Literal::str_starts_with(std::string_view const needle) const noexcept {
if (!this->is_string_like()) {
return TriBool::Err;
}
auto const s = this->lexical_form();
auto norm_needle = needle | una::ranges::views::utf8;
auto len = std::ranges::distance(norm_needle);
// TODO use std::ranges::starts_with as soon as c++ 23 arrives
return std::ranges::equal(norm_needle, s | una::ranges::views::utf8 | una::views::take(len));
}
Literal Literal::as_str_starts_with(std::string_view const needle, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
auto const res = this->str_starts_with(needle);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::as_str_starts_with(Literal const &needle, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
if (!needle.is_string_like()) {
return Literal{};
}
if (needle.datatype_eq<datatypes::rdf::LangString>() && this->language_tag() != needle.language_tag()) {
return Literal{};
}
auto const res = this->str_starts_with(needle.lexical_form());
return Literal::make_boolean(res, select_node_storage(node_storage));
}
TriBool Literal::str_ends_with(std::string_view needle) const noexcept {
if (!this->is_string_like()) {
return TriBool::Err;
}
auto const s = this->lexical_form();
auto norm_needle = needle | una::views::utf8;
auto norm_this = s | una::views::utf8;
auto const len_needle = std::ranges::distance(norm_needle);
auto const len_this = std::ranges::distance(norm_this);
// TODO use std::ranges::ends_with as soon as c++ 23 arrives
if (len_needle > len_this)
return false;
return std::ranges::equal(norm_needle, norm_this | una::views::drop(len_this - len_needle));
}
Literal Literal::as_str_ends_with(std::string_view const needle, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
auto const res = this->str_ends_with(needle);
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::as_str_ends_with(Literal const &needle, storage::DynNodeStoragePtr node_storage) const noexcept {
if (this->null()) {
return Literal{};
}
if (!needle.is_string_like()) {
return Literal{};
}
if (needle.datatype_eq<datatypes::rdf::LangString>() && this->language_tag() != needle.language_tag()) {
return Literal{};
}
auto const res = this->str_ends_with(needle.lexical_form());
return Literal::make_boolean(res, select_node_storage(node_storage));
}
Literal Literal::uppercase(storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like()) {
return Literal{};
}
auto const s = this->lexical_form();
auto const upper = una::cases::to_uppercase_utf8(s);
return Literal::make_string_like_copy_lang_tag(upper, *this, select_node_storage(node_storage));
}
Literal Literal::lowercase(storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like()) {
return Literal{};
}
auto const s = this->lexical_form();
const auto lower = una::cases::to_lowercase_utf8(s);
return Literal::make_string_like_copy_lang_tag(lower, *this, select_node_storage(node_storage));
}
Literal Literal::concat(Literal const &other, storage::DynNodeStoragePtr node_storage) const {
if (this->null() || other.null()) {
return Literal{};
}
auto const this_lex = this->lexical_form();
auto const other_lex = other.lexical_form();
std::string combined;
combined.reserve(this_lex.size() + other_lex.size());
combined.append(this_lex);
combined.append(other_lex);
auto const needs_escape = lexical_form_needs_escape(combined); // TODO not optimal
if (this->datatype_eq<datatypes::rdf::LangString>() && other.datatype_eq<datatypes::rdf::LangString>()) {
if (auto const lang = this->language_tag(); lang == other.language_tag()) {
return Literal::make_lang_tagged_unchecked(combined, needs_escape, lang, select_node_storage(node_storage));
}
}
return Literal::make_simple_unchecked(combined, needs_escape, select_node_storage(node_storage));
}
Literal Literal::encode_for_uri(std::string_view string, storage::DynNodeStoragePtr node_storage) {
if (!una::is_valid_utf8(string))
return Literal{};
std::stringstream stream{};
stream << std::hex << std::setfill('0');
// note that ASCII is a subset of UTF-8
auto is_valid = [](const uint32_t cp) {
if (cp >= static_cast<uint32_t>(static_cast<unsigned char>('A')) && cp <= static_cast<uint32_t>(static_cast<unsigned char>('Z')))
return true;
if (cp >= static_cast<uint32_t>(static_cast<unsigned char>('a')) && cp <= static_cast<uint32_t>(static_cast<unsigned char>('z')))
return true;
if (cp >= static_cast<uint32_t>(static_cast<unsigned char>('0')) && cp <= static_cast<uint32_t>(static_cast<unsigned char>('9')))
return true;
if (cp == static_cast<uint32_t>(static_cast<unsigned char>('-')) || cp == static_cast<uint32_t>(static_cast<unsigned char>('_')) ||
cp == static_cast<uint32_t>(static_cast<unsigned char>('.')) || cp == static_cast<uint32_t>(static_cast<unsigned char>('~')))
return true;
return false;
};
for (const uint32_t cp : string | una::views::utf8) {
if (is_valid(cp)) {
stream << std::nouppercase << static_cast<char>(static_cast<unsigned char>(cp)); // all URI allowed characters are ASCII, so this cast is valid
} else {
const std::array<uint32_t, 1> data{cp};
const auto r = data | una::ranges::to_utf8<std::string>(); // at maximum 4 bytes + zero, guaranteed to fit into small string optimization
for (const char c : r) {
stream << '%';
stream << std::uppercase << std::setw(2) << static_cast<unsigned int>(static_cast<unsigned char>(c));
}
}
}
return make_simple_unchecked(stream.view(), false, node_storage);
}
Literal Literal::encode_for_uri(storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like())
return Literal{};
const auto s = this->lexical_form();
return encode_for_uri(s, select_node_storage(node_storage));
}
Literal Literal::substr(size_t start, size_t len, storage::DynNodeStoragePtr node_storage) const {
if (!this->is_string_like()) {
return Literal{};
}
auto const s = this->lexical_form();
auto substr = s | una::ranges::views::utf8 | una::views::drop(start) | una::views::take(len) | una::ranges::to_utf8<std::string>();
return Literal::make_string_like_copy_lang_tag(substr, *this, select_node_storage(node_storage));
}
Literal Literal::substr(Literal const &start, Literal const &len, storage::DynNodeStoragePtr node_storage) const {
using namespace datatypes;
if (!this->is_string_like()) {
return Literal{};
}
auto const start_double = start.cast<xsd::Double>(node_storage);
if (start_double.null()) {
return Literal{};
}
auto const len_double = len.cast<xsd::Double>(node_storage);
if (len_double.null()) {
return Literal{};
}
auto const start_val = start_double.value<xsd::Double>();
if (std::isnan(start_val) || (std::isinf(start_val) && start_val > 0)) [[unlikely]] {
return Literal::make_string_like_copy_lang_tag("", *this, select_node_storage(node_storage));
}
auto const len_val = len_double.value<xsd::Double>();
if (std::isnan(len_val) || len_val <= 0) {
return Literal::make_string_like_copy_lang_tag("", *this, select_node_storage(node_storage));
}
auto const start_ix = static_cast<size_t>(std::round(std::max(0.0, start_val - 1.0)));
if (std::isinf(len_val)) {
return this->substr(start_ix);
}
auto const len_ix = static_cast<size_t>(std::round(len_val) - (start_val < 1.0 ? std::round(1.0 - start_val) : 0.0));
return this->substr(start_ix, len_ix, node_storage);
}
Literal Literal::hash_with(char const *alg, storage::DynNodeStoragePtr node_storage) const {
if (this->handle_.node_id().literal_type() != datatypes::xsd::String::fixed_id)
return Literal{};
auto const s = this->lexical_form();
unsigned char hash_buffer[EVP_MAX_MD_SIZE];
size_t len = 0;
if (!EVP_Q_digest(nullptr, alg, nullptr, s.data(), s.size(), hash_buffer, &len))
return Literal{};
std::span<std::byte const> const bytes{reinterpret_cast<std::byte const *>(hash_buffer), len};
auto const lex = writer::StringWriter::oneshot([bytes](auto &w) {
return datatypes::xsd::HexBinary::cpp_type::serialize_hash(bytes, w);
});
return Literal::make_simple(lex, select_node_storage(node_storage));
}
Literal Literal::md5(storage::DynNodeStoragePtr node_storage) const {
return this->hash_with("MD5", node_storage);
}
Literal Literal::sha1(storage::DynNodeStoragePtr node_storage) const {
return this->hash_with("SHA1", node_storage);
}
Literal Literal::sha256(storage::DynNodeStoragePtr node_storage) const {
return this->hash_with("SHA2-256", node_storage);
}
Literal Literal::sha384(storage::DynNodeStoragePtr node_storage) const {
return this->hash_with("SHA2-384", node_storage);
}
Literal Literal::sha512(storage::DynNodeStoragePtr node_storage) const {
return this->hash_with("SHA2-512", node_storage);
}
Literal Literal::now(storage::DynNodeStoragePtr node_storage) {
auto n = std::chrono::floor<std::chrono::milliseconds>(std::chrono::system_clock::now());
Timezone tz{};
TimePoint t = tz.to_local(n);
OptionalTimezone opt = std::nullopt;
return make_typed_from_value<datatypes::xsd::DateTime>(std::make_pair(t, opt), node_storage);
}
std::optional<Year> Literal::year() const noexcept {
if (!datatype_eq<datatypes::xsd::DateTime>() && !datatype_eq<datatypes::xsd::DateTimeStamp>() && !datatype_eq<datatypes::xsd::Date>()
&& !datatype_eq<datatypes::xsd::GYearMonth>() && !datatype_eq<datatypes::xsd::GYear>())
return std::nullopt;
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto [date, _] = util::deconstruct_timepoint(casted->first);
return date.year();
}
Literal Literal::as_year(storage::DynNodeStoragePtr node_storage) const {
auto r = this->year();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::Integer>(static_cast<int64_t>(*r), select_node_storage(node_storage));
}
std::optional<std::chrono::month> Literal::month() const noexcept {
if (!datatype_eq<datatypes::xsd::DateTime>() && !datatype_eq<datatypes::xsd::DateTimeStamp>() && !datatype_eq<datatypes::xsd::Date>()
&& !datatype_eq<datatypes::xsd::GYearMonth>() && !datatype_eq<datatypes::xsd::GMonthDay>() && !datatype_eq<datatypes::xsd::GMonth>())
return std::nullopt;
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto [date, _] = util::deconstruct_timepoint(casted->first);
return date.month();
}
Literal Literal::as_month(storage::DynNodeStoragePtr node_storage) const {
auto r = this->month();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::Integer>(static_cast<unsigned int>(*r), select_node_storage(node_storage));
}
std::optional<std::chrono::day> Literal::day() const noexcept {
if (!datatype_eq<datatypes::xsd::DateTime>() && !datatype_eq<datatypes::xsd::DateTimeStamp>() && !datatype_eq<datatypes::xsd::Date>()
&& !datatype_eq<datatypes::xsd::GMonthDay>() && !datatype_eq<datatypes::xsd::GDay>())
return std::nullopt;
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto [date, _] = util::deconstruct_timepoint(casted->first);
return date.day();
}
Literal Literal::as_day(storage::DynNodeStoragePtr node_storage) const {
auto r = this->day();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::Integer>(static_cast<unsigned int>(*r), select_node_storage(node_storage));
}
std::optional<std::chrono::hours> Literal::hours() const noexcept {
if (!datatype_eq<datatypes::xsd::DateTime>() && !datatype_eq<datatypes::xsd::DateTimeStamp>() && !datatype_eq<datatypes::xsd::Time>())
return std::nullopt;
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto [_, time] = util::deconstruct_timepoint(casted->first);
return std::chrono::hh_mm_ss{std::chrono::duration_cast<std::chrono::nanoseconds>(time)}.hours();
}
Literal Literal::as_hours(storage::DynNodeStoragePtr node_storage) const {
auto r = this->hours();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::Integer>(r->count(), select_node_storage(node_storage));
}
std::optional<std::chrono::minutes> Literal::minutes() const noexcept {
if (!datatype_eq<datatypes::xsd::DateTime>() && !datatype_eq<datatypes::xsd::DateTimeStamp>() && !datatype_eq<datatypes::xsd::Time>())
return std::nullopt;
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto [_, time] = util::deconstruct_timepoint(casted->first);
return std::chrono::hh_mm_ss{std::chrono::duration_cast<std::chrono::nanoseconds>(time)}.minutes();
}
Literal Literal::as_minutes(storage::DynNodeStoragePtr node_storage) const {
auto r = this->minutes();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::Integer>(r->count(), select_node_storage(node_storage));
}
std::optional<std::chrono::nanoseconds> Literal::seconds() const noexcept {
if (!datatype_eq<datatypes::xsd::DateTime>() && !datatype_eq<datatypes::xsd::DateTimeStamp>() && !datatype_eq<datatypes::xsd::Time>())
return std::nullopt;
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto [_, t] = util::deconstruct_timepoint(casted->first);
std::chrono::hh_mm_ss const time{std::chrono::duration_cast<std::chrono::nanoseconds>(t)};
return time.seconds() + time.subseconds();
}
Literal Literal::as_seconds(storage::DynNodeStoragePtr node_storage) const {
auto r = this->seconds();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::Decimal>(rdf4cpp::BigDecimal<>{r->count(), 9}, select_node_storage(node_storage));
}
std::optional<Timezone> Literal::timezone() const noexcept {
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto tz = casted->second;
return tz;
}
Literal Literal::as_timezone(storage::DynNodeStoragePtr node_storage) const {
auto r = this->timezone();
if (!r.has_value())
return Literal{};
return Literal::make_typed_from_value<datatypes::xsd::DayTimeDuration>(r->offset, select_node_storage(node_storage));
}
std::optional<std::string> Literal::tz() const noexcept {
auto casted = this->cast_to_value<datatypes::xsd::DateTime>();
if (!casted.has_value())
return std::nullopt;
auto tz = casted->second;
if (!tz.has_value())
return "";
return tz->to_canonical_string();
}
Literal Literal::as_tz(storage::DynNodeStoragePtr node_storage) const {
auto r = this->tz();
if (!r.has_value())
return Literal{};
return Literal::make_simple_unchecked(*r, false, select_node_storage(node_storage));
}
std::string normalize_unicode(std::string_view utf8) {
return una::norm::to_nfc_utf8(utf8);
}
bool lang_matches(std::string_view const lang_tag, std::string_view const lang_range) noexcept {
if (lang_range.empty()) {
return lang_tag.empty();
}
if (lang_range == "*") {
return !lang_tag.empty();
}
auto const lang_ci = util::CiStringView{lang_tag.data(), lang_tag.size()};
auto const lang_range_ci = util::CiStringView{lang_range.data(), lang_range.size()};
// case-insensitive comparison
return lang_ci.starts_with(lang_range_ci) && (lang_ci.size() == lang_range_ci.size() || lang_ci[lang_range_ci.size()] == '-');
}
Literal lang_matches(Literal const &lang_tag, Literal const &lang_range, storage::DynNodeStoragePtr node_storage) noexcept {
if (lang_tag.null() || lang_range.null()) {
return Literal{};
}
if (!lang_tag.datatype_eq<datatypes::xsd::String>() || !lang_range.datatype_eq<datatypes::xsd::String>()) {
return Literal{};
}
auto const res = lang_matches(lang_tag.lexical_form(), lang_range.lexical_form());
return Literal::make_boolean(res, lang_tag.select_node_storage(node_storage));
}
Literal Literal::math_pi(storage::DynNodeStoragePtr node_storage) {
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::numbers::pi, node_storage);
}
#define CPP_DOUBLE_OR_RETURN_NULL(source_expr, var_name) \
auto const var_name##_opt = [&source_obj = (source_expr)]() -> std::optional<double> { \
if (source_obj.null()) { \
return std::nullopt; \
} \
if (!source_obj.datatype_eq<datatypes::xsd::Double>()) { \
return source_obj.cast_to_value<datatypes::xsd::Double>(); \
} \
return source_obj.value<datatypes::xsd::Double>(); \
}(); \
if (!var_name##_opt.has_value()) { \
return {}; \
} \
auto const var_name = *var_name##_opt;
Literal Literal::math_exp(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::exp(th), select_node_storage(node_storage));
}
Literal Literal::math_exp10(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::pow(10.0, th), select_node_storage(node_storage));
}
Literal Literal::math_log(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::log(th), select_node_storage(node_storage));
}
Literal Literal::math_log10(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::log10(th), select_node_storage(node_storage));
}
Literal Literal::math_pow(Literal exp, storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
CPP_DOUBLE_OR_RETURN_NULL(exp, ex);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::pow(th, ex), select_node_storage(node_storage));
}
Literal Literal::math_sqrt(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::sqrt(th), select_node_storage(node_storage));
}
Literal Literal::math_sin(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::sin(th), select_node_storage(node_storage));
}
Literal Literal::math_cos(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::cos(th), select_node_storage(node_storage));
}
Literal Literal::math_tan(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::tan(th), select_node_storage(node_storage));
}
Literal Literal::math_asin(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::asin(th), select_node_storage(node_storage));
}
Literal Literal::math_acos(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::acos(th), select_node_storage(node_storage));
}
Literal Literal::math_atan(storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::atan(th), select_node_storage(node_storage));
}
Literal Literal::math_atan2(Literal y, storage::DynNodeStoragePtr node_storage) const {
CPP_DOUBLE_OR_RETURN_NULL(*this, th);
CPP_DOUBLE_OR_RETURN_NULL(y, yd);
return Literal::make_typed_from_value<datatypes::xsd::Double>(std::atan2(th, yd), select_node_storage(node_storage));
}
inline namespace shorthands {
Literal operator""_xsd_string(char const *str, size_t const len) {
return Literal::make_simple(std::string_view{str, len});
}
Literal operator""_xsd_double(long double d) {
return Literal::make_typed_from_value<datatypes::xsd::Double>(static_cast<datatypes::xsd::Double::cpp_type>(d));
}
Literal operator""_xsd_float(long double d) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::Float>(static_cast<datatypes::xsd::Float::cpp_type>(d));
}
Literal operator""_xsd_decimal(char const *str, size_t const len) {
return Literal::make_typed<datatypes::xsd::Decimal>(std::string_view{str, len});
}
Literal operator""_xsd_integer(unsigned long long int i) {
return Literal::make_typed_from_value<datatypes::xsd::Integer>(i);
}
Literal operator""_xsd_byte(unsigned long long int i) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::Byte>(static_cast<datatypes::xsd::Byte::cpp_type>(i));
}
Literal operator""_xsd_ubyte(unsigned long long int i) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::UnsignedByte>(static_cast<datatypes::xsd::UnsignedByte::cpp_type>(i));
}
Literal operator""_xsd_short(unsigned long long int i) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::Short>(static_cast<datatypes::xsd::Short::cpp_type>(i));
}
Literal operator""_xsd_ushort(unsigned long long int i) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::UnsignedShort>(static_cast<datatypes::xsd::UnsignedShort::cpp_type>(i));
}
Literal operator""_xsd_int(unsigned long long int i) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::Int>(static_cast<datatypes::xsd::Int::cpp_type>(i));
}
Literal operator""_xsd_uint(unsigned long long int i) noexcept {
return Literal::make_typed_from_value<datatypes::xsd::UnsignedInt>(static_cast<datatypes::xsd::UnsignedInt::cpp_type>(i));
}
Literal operator""_xsd_long(unsigned long long int i) {
return Literal::make_typed_from_value<datatypes::xsd::Long>(static_cast<datatypes::xsd::Long::cpp_type>(i));
}
Literal operator""_xsd_ulong(unsigned long long int i) {
return Literal::make_typed_from_value<datatypes::xsd::UnsignedLong>(static_cast<datatypes::xsd::UnsignedLong::cpp_type>(i));
}
} // namespace shorthands
} // namespace rdf4cpp
auto std::formatter<rdf4cpp::Literal>::format(rdf4cpp::Literal n, format_context &ctx) const -> decltype(ctx.out()) {
rdf4cpp::writer::BufOutputIteratorWriter w{ctx.out()};
n.serialize(w);
w.finalize();
return w.buffer().iter;
}