From 89612ec05f596d135640413e093251fb9691ca14 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 30 May 2021 12:23:07 -0400 Subject: Add support for converting literals to canonical form --- NEWS | 1 + doc/serdi.1 | 14 ++- include/serd/serd.h | 27 +++++ meson.build | 1 + src/canon.c | 192 ++++++++++++++++++++++++++++++++++++ src/serdi.c | 20 +++- src/string.c | 2 + src/string_utils.h | 8 +- test/canon/bad-boolean.ttl | 5 + test/canon/bad-decimal-leading.ttl | 4 + test/canon/bad-decimal-trailing.ttl | 4 + test/canon/bad-empty-boolean.ttl | 5 + test/canon/bad-integer-leading.ttl | 4 + test/canon/bad-integer-trailing.ttl | 4 + test/canon/manifest.ttl | 58 +++++++++++ test/canon/test-canon.nt | 70 +++++++++++++ test/canon/test-canon.ttl | 76 ++++++++++++++ test/meson.build | 8 ++ test/test_string.c | 2 +- 19 files changed, 495 insertions(+), 10 deletions(-) create mode 100644 src/canon.c create mode 100644 test/canon/bad-boolean.ttl create mode 100644 test/canon/bad-decimal-leading.ttl create mode 100644 test/canon/bad-decimal-trailing.ttl create mode 100644 test/canon/bad-empty-boolean.ttl create mode 100644 test/canon/bad-integer-leading.ttl create mode 100644 test/canon/bad-integer-trailing.ttl create mode 100644 test/canon/manifest.ttl create mode 100644 test/canon/test-canon.nt create mode 100644 test/canon/test-canon.ttl diff --git a/NEWS b/NEWS index 21dad0a1..dec4d930 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ serd (1.0.1) unstable; * Add extensible logging API * Add model for storing statements in memory * Add option for writing terse output without newlines + * Add support for converting literals to canonical form * Add support for parsing variables * Add support for writing terse collections * Add support for xsd:float and xsd:double literals diff --git a/doc/serdi.1 b/doc/serdi.1 index e9d70857..59eb67ce 100644 --- a/doc/serdi.1 +++ b/doc/serdi.1 @@ -6,7 +6,7 @@ .Nd read, transform, and write RDF data .Sh SYNOPSIS .Nm serdi -.Op Fl abefhlmqtvx +.Op Fl Cabefhlmqtvx .Op Fl I Ar base .Op Fl c Ar prefix .Op Fl i Ar syntax @@ -35,6 +35,18 @@ or transform URIs and blank node IDs. The options are as follows: .Pp .Bl -tag -compact -width 3n +.It Fl C +Convert literals to canonical form. +Literals with supported XSD datatypes will be parsed and rewritten canonically. +All numeric datatypes are supported, as well as +.Vt boolean , +.Vt duration , +.Vt datetime , +.Vt time , +.Vt hexBinary , +and +.Vt base64Binary . +.Pp .It Fl I Ar base Input base URI. Relative URI references in the input will be resolved against this. diff --git a/include/serd/serd.h b/include/serd/serd.h index efa48543..fad9d070 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -217,6 +217,7 @@ typedef enum { SERD_ERR_BAD_CALL, ///< Invalid call SERD_ERR_BAD_URI, ///< Invalid or unresolved URI SERD_ERR_BAD_INDEX, ///< No optimal model index available + SERD_ERR_INVALID, ///< Invalid data } SerdStatus; /** @@ -1937,6 +1938,32 @@ SerdStatus serd_sink_write_end(const SerdSink* SERD_NONNULL sink, const SerdNode* SERD_NONNULL node); +/** + @} + @defgroup serd_canon Canon + @{ +*/ + +/// Flags that control canonical node transformation +typedef enum { + SERD_CANON_LAX = 1u << 0u, ///< Tolerate and pass through invalid input +} SerdCanonFlag; + +/// Bitwise OR of SerdCanonFlag values +typedef uint32_t SerdCanonFlags; + +/** + Return a new sink that transforms literals to canonical form where possible. + + The returned sink acts like `target` in all respects, except literal nodes + in statements may be modified from the original. +*/ +SERD_API +SerdSink* SERD_ALLOCATED +serd_canon_new(const SerdWorld* SERD_NULLABLE world, + const SerdSink* SERD_NONNULL target, + SerdCanonFlags flags); + /** @} @defgroup serd_env Environment diff --git a/meson.build b/meson.build index 5197f448..25cce6d0 100644 --- a/meson.build +++ b/meson.build @@ -86,6 +86,7 @@ c_header = files('include/serd/serd.h') sources = [ 'src/byte_sink.c', 'src/byte_source.c', + 'src/canon.c', 'src/caret.c', 'src/compare.c', 'src/cursor.c', diff --git a/src/canon.c b/src/canon.c new file mode 100644 index 00000000..84d20d0c --- /dev/null +++ b/src/canon.c @@ -0,0 +1,192 @@ +/* + Copyright 2019-2022 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "caret.h" +#include "namespaces.h" +#include "node.h" +#include "statement.h" +#include "string_utils.h" + +#include "exess/exess.h" +#include "serd/serd.h" + +#include +#include +#include + +typedef struct { + const SerdWorld* world; + const SerdSink* target; + SerdCanonFlags flags; +} SerdCanonData; + +static ExessResult +build_typed(SerdNode** const out, + const SerdNode* const SERD_NONNULL node, + const SerdNode* const SERD_NONNULL datatype) +{ + *out = NULL; + + const char* str = serd_node_string(node); + const char* datatype_uri = serd_node_string(datatype); + ExessResult r = {EXESS_SUCCESS, 0}; + + if (!strcmp(datatype_uri, NS_RDF "langString")) { + *out = serd_new_string(serd_node_string_view(node)); + return r; + } + + const ExessDatatype value_type = exess_datatype_from_uri(datatype_uri); + if (value_type == EXESS_NOTHING) { + return r; + } + + // Measure canonical form to know how much space to allocate for node + if ((r = exess_write_canonical(str, value_type, 0, NULL)).status) { + return r; + } + + // Allocate node + const size_t datatype_uri_len = serd_node_length(datatype); + const size_t datatype_size = serd_node_total_size(datatype); + const size_t len = serd_node_pad_length(r.count); + const size_t total_len = sizeof(SerdNode) + len + datatype_size; + SerdNode* const result = serd_node_malloc(total_len); + + result->length = r.count; + result->flags = SERD_HAS_DATATYPE; + result->type = SERD_LITERAL; + + // Write canonical form directly into node + exess_write_canonical(str, value_type, r.count + 1, serd_node_buffer(result)); + + SerdNode* const datatype_node = result + 1 + (len / sizeof(SerdNode)); + char* const datatype_buf = serd_node_buffer(datatype_node); + + datatype_node->length = datatype_uri_len; + datatype_node->type = SERD_URI; + memcpy(datatype_buf, datatype_uri, datatype_uri_len + 1); + + *out = result; + return r; +} + +static ExessResult +build_tagged(SerdNode** const out, + const SerdNode* const SERD_NONNULL node, + const SerdNode* const SERD_NONNULL language) +{ +#define MAX_LANG_LEN 48 // RFC5646 requires 35, RFC4646 recommends 42 + + const size_t node_len = serd_node_length(node); + const char* const lang = serd_node_string(language); + const size_t lang_len = serd_node_length(language); + if (lang_len > MAX_LANG_LEN) { + const ExessResult r = {EXESS_NO_SPACE, node_len}; + return r; + } + + // Convert language tag to lower-case + char canonical_lang[MAX_LANG_LEN] = {0}; + for (size_t i = 0u; i < lang_len; ++i) { + canonical_lang[i] = serd_to_lower(lang[i]); + } + + // Make a new literal that is otherwise identical + *out = serd_new_literal(serd_node_string_view(node), + serd_node_flags(node), + SERD_SUBSTRING(canonical_lang, lang_len)); + + const ExessResult r = {EXESS_SUCCESS, node_len}; + return r; + +#undef MAX_LANG_LEN +} + +static SerdStatus +serd_canon_on_statement(SerdCanonData* const data, + const SerdStatementFlags flags, + const SerdStatement* const statement) +{ + const SerdNode* const object = serd_statement_object(statement); + const SerdNode* const datatype = serd_node_datatype(object); + const SerdNode* const language = serd_node_language(object); + if (!datatype && !language) { + return serd_sink_write_statement(data->target, flags, statement); + } + + SerdNode* normo = NULL; + const ExessResult r = datatype ? build_typed(&normo, object, datatype) + : build_tagged(&normo, object, language); + + if (r.status) { + SerdCaret caret = {NULL, 0u, 0u}; + const bool lax = (data->flags & SERD_CANON_LAX); + + if (statement->caret) { + // Adjust column to point at the error within the literal + caret.file = statement->caret->file; + caret.line = statement->caret->line; + caret.col = statement->caret->col + 1 + (unsigned)r.count; + } + + serd_logf_at(data->world, + lax ? SERD_LOG_LEVEL_WARNING : SERD_LOG_LEVEL_ERROR, + statement->caret ? &caret : NULL, + "invalid literal (%s)", + exess_strerror(r.status)); + + if (!lax) { + return SERD_ERR_INVALID; + } + } + + if (!normo) { + return serd_sink_write_statement(data->target, flags, statement); + } + + const SerdStatus st = serd_sink_write(data->target, + flags, + statement->nodes[0], + statement->nodes[1], + normo, + statement->nodes[3]); + serd_node_free(normo); + return st; +} + +static SerdStatus +serd_canon_on_event(SerdCanonData* const data, const SerdEvent* const event) +{ + return (event->type == SERD_STATEMENT) + ? serd_canon_on_statement( + data, event->statement.flags, event->statement.statement) + : serd_sink_write_event(data->target, event); +} + +SerdSink* +serd_canon_new(const SerdWorld* const world, + const SerdSink* const target, + const SerdCanonFlags flags) +{ + SerdCanonData* const data = (SerdCanonData*)calloc(1, sizeof(SerdCanonData)); + + data->world = world; + data->target = target; + data->flags = flags; + + return serd_sink_new(data, (SerdEventFunc)serd_canon_on_event, free); +} diff --git a/src/serdi.c b/src/serdi.c index 97601b83..243be98e 100644 --- a/src/serdi.c +++ b/src/serdi.c @@ -56,6 +56,7 @@ print_usage(const char* const name, const bool error) fprintf(os, "Usage: %s [OPTION]... INPUT...\n", name); fprintf(os, "Read and write RDF syntax.\n"); fprintf(os, "Use - for INPUT to read from standard input.\n\n"); + fprintf(os, " -C Convert literals to canonical form.\n"); fprintf(os, " -I BASE_URI Input base URI.\n"); fprintf(os, " -a Write ASCII output if possible.\n"); fprintf(os, " -b Fast bulk output for large serialisations.\n"); @@ -153,6 +154,7 @@ main(int argc, char** argv) bool no_inline = false; bool osyntax_set = false; bool use_model = false; + bool canonical = false; bool quiet = false; size_t stack_size = 4194304; const char* input_string = NULL; @@ -169,7 +171,9 @@ main(int argc, char** argv) for (int o = 1; argv[a][o]; ++o) { const char opt = argv[a][o]; - if (opt == 'a') { + if (opt == 'C') { + canonical = true; + } else if (opt == 'a') { writer_flags |= SERD_WRITE_ASCII; } else if (opt == 'b') { bulk_write = true; @@ -337,7 +341,7 @@ main(int argc, char** argv) SerdModel* model = NULL; SerdSink* inserter = NULL; - const SerdSink* sink = NULL; + const SerdSink* out_sink = NULL; if (use_model) { const SerdModelFlags flags = (input_has_graphs ? SERD_STORE_GRAPHS : 0u); @@ -354,9 +358,16 @@ main(int argc, char** argv) } inserter = serd_inserter_new(model, NULL); - sink = inserter; + out_sink = inserter; } else { - sink = serd_writer_sink(writer); + out_sink = serd_writer_sink(writer); + } + + const SerdSink* sink = out_sink; + + SerdSink* canon = NULL; + if (canonical) { + sink = canon = serd_canon_new(world, out_sink, reader_flags); } if (quiet) { @@ -455,6 +466,7 @@ main(int argc, char** argv) serd_cursor_free(everything); } + serd_sink_free(canon); serd_sink_free(inserter); serd_model_free(model); serd_writer_free(writer); diff --git a/src/string.c b/src/string.c index 11b53050..6942b7b6 100644 --- a/src/string.c +++ b/src/string.c @@ -62,6 +62,8 @@ serd_strerror(const SerdStatus status) return "Invalid or unresolved URI"; case SERD_ERR_BAD_INDEX: return "No optimal model index available"; + case SERD_ERR_INVALID: + return "Invalid data"; } return "Unknown error"; diff --git a/src/string_utils.h b/src/string_utils.h index 54f7877c..5cf7ba8c 100644 --- a/src/string_utils.h +++ b/src/string_utils.h @@ -60,7 +60,7 @@ is_xdigit(const int c) } static inline bool -is_space(const char c) +is_space(const int c) { switch (c) { case ' ': @@ -89,16 +89,16 @@ is_windows_path(const char* path) } static inline char -serd_to_upper(const char c) +serd_to_lower(const char c) { - return (char)((c >= 'a' && c <= 'z') ? c - 32 : c); + return (char)((c >= 'A' && c <= 'Z') ? c + 32 : c); } static inline int serd_strncasecmp(const char* s1, const char* s2, size_t n) { for (; n > 0 && *s2; s1++, s2++, --n) { - if (serd_to_upper(*s1) != serd_to_upper(*s2)) { + if (serd_to_lower(*s1) != serd_to_lower(*s2)) { return ((*(const uint8_t*)s1 < *(const uint8_t*)s2) ? -1 : +1); } } diff --git a/test/canon/bad-boolean.ttl b/test/canon/bad-boolean.ttl new file mode 100644 index 00000000..c4fc3eb5 --- /dev/null +++ b/test/canon/bad-boolean.ttl @@ -0,0 +1,5 @@ +@base . +@prefix xsd: . + +[] " ja "^^xsd:boolean . + diff --git a/test/canon/bad-decimal-leading.ttl b/test/canon/bad-decimal-leading.ttl new file mode 100644 index 00000000..0d18eac7 --- /dev/null +++ b/test/canon/bad-decimal-leading.ttl @@ -0,0 +1,4 @@ +@base . +@prefix xsd: . + +[] " junk 1234.5678 "^^xsd:decimal . diff --git a/test/canon/bad-decimal-trailing.ttl b/test/canon/bad-decimal-trailing.ttl new file mode 100644 index 00000000..10882ef5 --- /dev/null +++ b/test/canon/bad-decimal-trailing.ttl @@ -0,0 +1,4 @@ +@base . +@prefix xsd: . + +[] " 1234.5678 junk "^^xsd:decimal . diff --git a/test/canon/bad-empty-boolean.ttl b/test/canon/bad-empty-boolean.ttl new file mode 100644 index 00000000..9a390c46 --- /dev/null +++ b/test/canon/bad-empty-boolean.ttl @@ -0,0 +1,5 @@ +@base . +@prefix xsd: . + +[] ""^^xsd:boolean . + diff --git a/test/canon/bad-integer-leading.ttl b/test/canon/bad-integer-leading.ttl new file mode 100644 index 00000000..80c1a6af --- /dev/null +++ b/test/canon/bad-integer-leading.ttl @@ -0,0 +1,4 @@ +@base . +@prefix xsd: . + +[] " junk 987654321 "^^xsd:integer . diff --git a/test/canon/bad-integer-trailing.ttl b/test/canon/bad-integer-trailing.ttl new file mode 100644 index 00000000..a94a9ec4 --- /dev/null +++ b/test/canon/bad-integer-trailing.ttl @@ -0,0 +1,4 @@ +@base . +@prefix xsd: . + +[] " 987654321 junk "^^xsd:integer . diff --git a/test/canon/manifest.ttl b/test/canon/manifest.ttl new file mode 100644 index 00000000..e2195212 --- /dev/null +++ b/test/canon/manifest.ttl @@ -0,0 +1,58 @@ +@prefix mf: . +@prefix rdfs: . +@prefix rdft: . + +<> + a mf:Manifest ; + rdfs:comment "Serd canonical literal test cases" ; + mf:entries ( + <#bad-boolean> + <#bad-decimal-leading> + <#bad-decimal-trailing> + <#bad-empty-boolean> + <#bad-integer-leading> + <#bad-integer-trailing> + <#test-canon> + ) . + +<#bad-boolean> + a rdft:TestTurtleNegativeEval ; + mf:name "bad-boolean" ; + rdfs:comment "Invalid xsd::boolean syntax" ; + mf:action . + +<#bad-decimal-leading> + a rdft:TestTurtleNegativeEval ; + mf:name "bad-decimal-leading" ; + rdfs:comment "Invalid xsd::decimal syntax (leading garbage)" ; + mf:action . + +<#bad-decimal-trailing> + a rdft:TestTurtleNegativeEval ; + mf:name "bad-decimal-trailing" ; + rdfs:comment "Invalid xsd::decimal syntax (trailing garbage)" ; + mf:action . + +<#bad-empty-boolean> + a rdft:TestTurtleNegativeEval ; + mf:name "bad-empty-boolean" ; + rdfs:comment "Invalid xsd::boolean syntax (no value)" ; + mf:action . + +<#bad-integer-leading> + a rdft:TestTurtleNegativeEval ; + mf:name "bad-integer-leading" ; + rdfs:comment "Invalid xsd::integer syntax (leading garbage)" ; + mf:action . + +<#bad-integer-trailing> + a rdft:TestTurtleNegativeEval ; + mf:name "bad-integer-trailing" ; + rdfs:comment "Invalid xsd::integer syntax (trailing garbage)" ; + mf:action . + +<#test-canon> + a rdft:TestTurtleEval ; + mf:name "test-canon" ; + mf:action ; + mf:result . diff --git a/test/canon/test-canon.nt b/test/canon/test-canon.nt new file mode 100644 index 00000000..ff492890 --- /dev/null +++ b/test/canon/test-canon.nt @@ -0,0 +1,70 @@ +_:b1 "false"^^ . +_:b1 "false"^^ . +_:b1 "true"^^ . +_:b1 "true"^^ . +_:b1 "1.0E2"^^ . +_:b1 "-1.0E2"^^ . +_:b1 "1.0E3"^^ . +_:b1 "-1.0E3"^^ . +_:b1 "9223372036854775807"^^ . +_:b1 "-9223372036854775808"^^ . +_:b1 "2147483647"^^ . +_:b1 "-2147483648"^^ . +_:b1 "32767"^^ . +_:b1 "-32768"^^ . +_:b1 "127"^^ . +_:b1 "-128"^^ . +_:b1 "1"^^ . +_:b1 "18446744073709551615"^^ . +_:b1 "1"^^ . +_:b1 "4294967295"^^ . +_:b1 "1"^^ . +_:b1 "65535"^^ . +_:b1 "1"^^ . +_:b1 "255"^^ . +_:b1 "0.0"^^ . +_:b1 "0.0"^^ . +_:b1 "-0.0"^^ . +_:b1 "36893488147419103232.0"^^ . +_:b1 "36893488147419103232.0"^^ . +_:b1 "36893488147419103232.0"^^ . +_:b1 "36893488147419103232.0"^^ . +_:b1 "36893488147419103232.0"^^ . +_:b1 "36893488147419103232.0"^^ . +_:b1 "36893488147419103232.123"^^ . +_:b1 "-36893488147419103232.0"^^ . +_:b1 "-36893488147419103232.0"^^ . +_:b1 "-36893488147419103232.0"^^ . +_:b1 "-36893488147419103232.0"^^ . +_:b1 "-36893488147419103232.123"^^ . +_:b1 "0.123"^^ . +_:b1 "0.123"^^ . +_:b1 "0.123"^^ . +_:b1 "0.123"^^ . +_:b1 "-0.123"^^ . +_:b1 "-0.123"^^ . +_:b1 "36893488147419103232"^^ . +_:b1 "36893488147419103232"^^ . +_:b1 "36893488147419103232"^^ . +_:b1 "36893488147419103232"^^ . +_:b1 "-36893488147419103232"^^ . +_:b1 "-36893488147419103232"^^ . +_:b1 "0"^^ . +_:b1 "-36893488147419103232"^^ . +_:b1 "-1"^^ . +_:b1 "-36893488147419103232"^^ . +_:b1 "0"^^ . +_:b1 "36893488147419103232"^^ . +_:b1 "1"^^ . +_:b1 "36893488147419103232"^^ . +_:b1 "no language tag" . +_:b1 "english"@en-ca . +_:b1 "P1Y6M"^^ . +_:b1 "12:15:01Z"^^ . +_:b1 "2004-04-12Z"^^ . +_:b1 "A1B7F080"^^ . +_:b1 "Zm9vYmF="^^ . +_:b1 "untyped" . +_:b1 . +_:b1 "notxsd"^^ . +_:b1 "unsupported"^^ . diff --git a/test/canon/test-canon.ttl b/test/canon/test-canon.ttl new file mode 100644 index 00000000..0d0b4682 --- /dev/null +++ b/test/canon/test-canon.ttl @@ -0,0 +1,76 @@ +@base . +@prefix rdf: . +@prefix xsd: . + +[ + " false "^^xsd:boolean , + " 0 "^^xsd:boolean , + " true "^^xsd:boolean , + " 1 "^^xsd:boolean ; + " +0100.0 "^^xsd:float , + " -0100.0 "^^xsd:float , + " +01000.0 "^^xsd:double , + " -01000.0 "^^xsd:double ; + " +09223372036854775807 "^^xsd:long , + " -09223372036854775808 "^^xsd:long , + " +02147483647 "^^xsd:int , + " -02147483648 "^^xsd:int , + " +032767 "^^xsd:short , + " -032768 "^^xsd:short , + " +0127 "^^xsd:byte , + " -0128 "^^xsd:byte , + " 01 "^^xsd:unsignedLong , + " 018446744073709551615 "^^xsd:unsignedLong , + " 01 "^^xsd:unsignedInt , + " 04294967295 "^^xsd:unsignedInt , + " 01 "^^xsd:unsignedShort , + " 065535 "^^xsd:unsignedShort , + " 01 "^^xsd:unsignedByte , + " 0255 "^^xsd:unsignedByte ; + " 00 "^^xsd:decimal , + " +0 "^^xsd:decimal , + " -0 "^^xsd:decimal , + " 36893488147419103232 "^^xsd:decimal , + " 0036893488147419103232 "^^xsd:decimal , + " +36893488147419103232 "^^xsd:decimal , + " +0036893488147419103232 "^^xsd:decimal , + " +0036893488147419103232. "^^xsd:decimal , + " +0036893488147419103232.00 "^^xsd:decimal , + " +0036893488147419103232.12300 "^^xsd:decimal , + " -36893488147419103232 "^^xsd:decimal , + " -0036893488147419103232 "^^xsd:decimal , + " -0036893488147419103232. "^^xsd:decimal , + " -0036893488147419103232.00 "^^xsd:decimal , + " -0036893488147419103232.12300 "^^xsd:decimal , + " 00.12300 "^^xsd:decimal , + " .12300 "^^xsd:decimal , + " +.12300 "^^xsd:decimal , + " +00.12300 "^^xsd:decimal , + " -.12300 "^^xsd:decimal , + " -00.12300 "^^xsd:decimal ; + " 36893488147419103232 "^^xsd:integer , + " 0036893488147419103232 "^^xsd:integer , + " +36893488147419103232 "^^xsd:integer , + " +0036893488147419103232 "^^xsd:integer , + " -36893488147419103232 "^^xsd:integer , + " -0036893488147419103232 "^^xsd:integer , + " 00 "^^xsd:nonPositiveInteger , + " -036893488147419103232 "^^xsd:nonPositiveInteger , + " -01 "^^xsd:negativeInteger , + " -036893488147419103232 "^^xsd:negativeInteger , + " 00 "^^xsd:nonNegativeInteger , + " 036893488147419103232 "^^xsd:nonNegativeInteger , + " +01 "^^xsd:positiveInteger , + " 036893488147419103232 "^^xsd:positiveInteger ; + "no language tag"^^rdf:langString ; + "english"@EN-CA ; +