eyeling 1.28.5 → 1.28.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,7 @@ const {
26
26
  LIST_NS,
27
27
  LOG_NS,
28
28
  STRING_NS,
29
+ DT_NS,
29
30
  Literal,
30
31
  Iri,
31
32
  Var,
@@ -175,7 +176,7 @@ function __buildBuiltinRegistrationApi() {
175
176
  literalsEquivalentAsXsdString,
176
177
  materializeRdfLists,
177
178
  terms: { Literal, Iri, Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, Rule },
178
- ns: { RDF_NS, XSD_NS, CRYPTO_NS, MATH_NS, TIME_NS, LIST_NS, LOG_NS, STRING_NS },
179
+ ns: { RDF_NS, XSD_NS, CRYPTO_NS, MATH_NS, TIME_NS, LIST_NS, LOG_NS, STRING_NS, DT_NS },
179
180
  };
180
181
 
181
182
  __builtinApiSingleton = __freezeBuiltinApi(api);
@@ -912,6 +913,63 @@ const XSD_DOUBLE_DT = XSD_NS + 'double';
912
913
  const XSD_FLOAT_DT = XSD_NS + 'float';
913
914
  const XSD_INTEGER_DT = XSD_NS + 'integer';
914
915
 
916
+ const XSD_STRING_DT = XSD_NS + 'string';
917
+ const XSD_BOOLEAN_DT = XSD_NS + 'boolean';
918
+ const XSD_DATETIME_DT = XSD_NS + 'dateTime';
919
+ const XSD_DATETIME_STAMP_DT = XSD_NS + 'dateTimeStamp';
920
+ const XSD_HEX_BINARY_DT = XSD_NS + 'hexBinary';
921
+ const XSD_BASE64_BINARY_DT = XSD_NS + 'base64Binary';
922
+ const XSD_ANY_URI_DT = XSD_NS + 'anyURI';
923
+ const RDF_LANGSTRING_DT = RDF_NS + 'langString';
924
+
925
+ const XSD_STRING_LIKE_DTS = new Set([
926
+ XSD_STRING_DT,
927
+ XSD_NS + 'normalizedString',
928
+ XSD_NS + 'token',
929
+ XSD_NS + 'language',
930
+ XSD_NS + 'Name',
931
+ XSD_NS + 'NCName',
932
+ XSD_NS + 'NMTOKEN',
933
+ ]);
934
+
935
+ const XSD_INTEGER_BOUNDS = new Map([
936
+ [XSD_NS + 'nonPositiveInteger', [null, 0n]],
937
+ [XSD_NS + 'negativeInteger', [null, -1n]],
938
+ [XSD_NS + 'long', [-(2n ** 63n), 2n ** 63n - 1n]],
939
+ [XSD_NS + 'int', [-(2n ** 31n), 2n ** 31n - 1n]],
940
+ [XSD_NS + 'short', [-(2n ** 15n), 2n ** 15n - 1n]],
941
+ [XSD_NS + 'byte', [-(2n ** 7n), 2n ** 7n - 1n]],
942
+ [XSD_NS + 'nonNegativeInteger', [0n, null]],
943
+ [XSD_NS + 'unsignedLong', [0n, 2n ** 64n - 1n]],
944
+ [XSD_NS + 'unsignedInt', [0n, 2n ** 32n - 1n]],
945
+ [XSD_NS + 'unsignedShort', [0n, 2n ** 16n - 1n]],
946
+ [XSD_NS + 'unsignedByte', [0n, 2n ** 8n - 1n]],
947
+ [XSD_NS + 'positiveInteger', [1n, null]],
948
+ ]);
949
+
950
+ const XSD_DATATYPE_BUILTIN_DTS = new Set([
951
+ RDF_LANGSTRING_DT,
952
+ XSD_STRING_DT,
953
+ XSD_NS + 'normalizedString',
954
+ XSD_NS + 'token',
955
+ XSD_NS + 'language',
956
+ XSD_NS + 'Name',
957
+ XSD_NS + 'NCName',
958
+ XSD_NS + 'NMTOKEN',
959
+ XSD_BOOLEAN_DT,
960
+ XSD_DECIMAL_DT,
961
+ XSD_INTEGER_DT,
962
+ XSD_FLOAT_DT,
963
+ XSD_DOUBLE_DT,
964
+ XSD_HEX_BINARY_DT,
965
+ XSD_BASE64_BINARY_DT,
966
+ XSD_ANY_URI_DT,
967
+ XSD_DATETIME_DT,
968
+ XSD_DATETIME_STAMP_DT,
969
+ ...XSD_INTEGER_BOUNDS.keys(),
970
+ ]);
971
+
972
+
915
973
  // Integer-derived datatypes from XML Schema Part 2 (and commonly used ones).
916
974
  const XSD_INTEGER_DERIVED_DTS = new Set([
917
975
  XSD_INTEGER_DT,
@@ -967,6 +1025,537 @@ function inferDatatypeForShorthandLexical(lex) {
967
1025
  return null;
968
1026
  }
969
1027
 
1028
+
1029
+ function extractLiteralLanguageTag(litVal) {
1030
+ if (typeof litVal !== 'string' || !literalHasLangTag(litVal)) return null;
1031
+ const lastQuote = litVal.lastIndexOf('"');
1032
+ if (lastQuote < 0) return null;
1033
+ const after = lastQuote + 1;
1034
+ if (after >= litVal.length || litVal[after] !== '@') return null;
1035
+ const tag = litVal.slice(after + 1);
1036
+ if (!/^[A-Za-z]+(?:-[A-Za-z0-9]+)*(?:--(?:ltr|rtl))?$/.test(tag)) return null;
1037
+ return tag;
1038
+ }
1039
+
1040
+ function literalLexicalValue(litVal) {
1041
+ const [lex] = literalParts(litVal);
1042
+ if (isQuotedLexical(lex)) return decodeN3StringEscapes(stripQuotes(lex));
1043
+ return typeof lex === 'string' ? lex : String(lex);
1044
+ }
1045
+
1046
+ function literalDatatypeIri(t) {
1047
+ if (!(t instanceof Literal)) return null;
1048
+ if (literalHasLangTag(t.value)) return RDF_LANGSTRING_DT;
1049
+
1050
+ const [lex, dt] = literalParts(t.value);
1051
+ if (dt !== null) return dt;
1052
+ if (isPlainStringLiteralValue(t.value)) return XSD_STRING_DT;
1053
+
1054
+ return inferDatatypeForShorthandLexical(lex) || XSD_STRING_DT;
1055
+ }
1056
+
1057
+ function isSupportedDatatypeIri(dt) {
1058
+ return XSD_DATATYPE_BUILTIN_DTS.has(dt);
1059
+ }
1060
+
1061
+ function makeDatatypeTypedLiteral(lexical, dt) {
1062
+ return internLiteral(`${JSON.stringify(String(lexical))}^^<${dt}>`);
1063
+ }
1064
+
1065
+ function makeLangStringLiteral(lexical, lang) {
1066
+ return internLiteral(`${JSON.stringify(String(lexical))}@${String(lang).toLowerCase()}`);
1067
+ }
1068
+
1069
+ function canonicalIntegerLex(v) {
1070
+ return v === 0n ? '0' : v.toString();
1071
+ }
1072
+
1073
+ function canonicalDecimalLexFromParts(num, scale) {
1074
+ if (num === 0n) return '0.0';
1075
+ const neg = num < 0n;
1076
+ let digits = (neg ? -num : num).toString();
1077
+ if (scale > 0) {
1078
+ while (digits.length <= scale) digits = `0${digits}`;
1079
+ }
1080
+ let intPart = scale > 0 ? digits.slice(0, digits.length - scale) : digits;
1081
+ let fracPart = scale > 0 ? digits.slice(digits.length - scale) : '';
1082
+ intPart = intPart.replace(/^0+(?=\d)/, '') || '0';
1083
+ fracPart = fracPart.replace(/0+$/g, '');
1084
+ if (!fracPart) fracPart = '0';
1085
+ return `${neg ? '-' : ''}${intPart}.${fracPart}`;
1086
+ }
1087
+
1088
+ function canonicalFloatDoubleLex(n) {
1089
+ const special = formatXsdFloatSpecialLex(n);
1090
+ if (special !== null) return special;
1091
+ if (Object.is(n, -0)) return '0';
1092
+ return String(n).replace('e', 'E');
1093
+ }
1094
+
1095
+ function parseStringLikeDatatypeValue(lexical, dt) {
1096
+ let value = String(lexical);
1097
+
1098
+ if (dt === XSD_NS + 'normalizedString') {
1099
+ value = value.replace(/[\t\n\r]/g, ' ');
1100
+ } else if (dt !== XSD_STRING_DT) {
1101
+ value = value.replace(/[\t\n\r ]+/g, ' ').trim();
1102
+ }
1103
+
1104
+ if (dt === XSD_NS + 'language') {
1105
+ if (!/^[A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*$/.test(value)) return null;
1106
+ value = value.toLowerCase();
1107
+ } else if (dt === XSD_NS + 'Name') {
1108
+ if (!/^[:_\p{L}][:_\p{L}\p{N}.\-\u00B7\u0300-\u036F\u203F-\u2040]*$/u.test(value)) return null;
1109
+ } else if (dt === XSD_NS + 'NCName') {
1110
+ if (!/^[_\p{L}][_\p{L}\p{N}.\-\u00B7\u0300-\u036F\u203F-\u2040]*$/u.test(value)) return null;
1111
+ } else if (dt === XSD_NS + 'NMTOKEN') {
1112
+ if (!/^[:_\p{L}\p{N}.\-\u00B7\u0300-\u036F\u203F-\u2040]+$/u.test(value)) return null;
1113
+ }
1114
+
1115
+ return {
1116
+ family: 'string',
1117
+ dt,
1118
+ value,
1119
+ canonicalLex: value,
1120
+ canonicalLiteral: makeDatatypeTypedLiteral(value, dt),
1121
+ comparable: true,
1122
+ };
1123
+ }
1124
+
1125
+ function parseBooleanDatatypeValue(lexical) {
1126
+ const s = String(lexical);
1127
+ if (s === 'true' || s === '1') {
1128
+ return {
1129
+ family: 'boolean',
1130
+ dt: XSD_BOOLEAN_DT,
1131
+ value: true,
1132
+ canonicalLex: 'true',
1133
+ canonicalLiteral: makeDatatypeTypedLiteral('true', XSD_BOOLEAN_DT),
1134
+ comparable: true,
1135
+ };
1136
+ }
1137
+ if (s === 'false' || s === '0') {
1138
+ return {
1139
+ family: 'boolean',
1140
+ dt: XSD_BOOLEAN_DT,
1141
+ value: false,
1142
+ canonicalLex: 'false',
1143
+ canonicalLiteral: makeDatatypeTypedLiteral('false', XSD_BOOLEAN_DT),
1144
+ comparable: true,
1145
+ };
1146
+ }
1147
+ return null;
1148
+ }
1149
+
1150
+ function parseIntegerDatatypeValue(lexical, dt) {
1151
+ const s = String(lexical);
1152
+ if (!/^[+-]?\d+$/.test(s)) return null;
1153
+ let value;
1154
+ try {
1155
+ value = BigInt(s);
1156
+ } catch (_) {
1157
+ return null;
1158
+ }
1159
+
1160
+ const bounds = XSD_INTEGER_BOUNDS.get(dt);
1161
+ if (bounds) {
1162
+ const [min, max] = bounds;
1163
+ if (min !== null && value < min) return null;
1164
+ if (max !== null && value > max) return null;
1165
+ }
1166
+
1167
+ const canonicalLex = canonicalIntegerLex(value);
1168
+ return {
1169
+ family: 'numeric',
1170
+ numericKind: 'decimalExact',
1171
+ dt,
1172
+ decimalNum: value,
1173
+ decimalScale: 0,
1174
+ value,
1175
+ canonicalLex,
1176
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1177
+ comparable: true,
1178
+ };
1179
+ }
1180
+
1181
+ function parseDecimalDatatypeValue(lexical) {
1182
+ const parsed = parseXsdDecimalToBigIntScale(String(lexical));
1183
+ if (!parsed) return null;
1184
+ const canonicalLex = canonicalDecimalLexFromParts(parsed.num, parsed.scale);
1185
+ return {
1186
+ family: 'numeric',
1187
+ numericKind: 'decimalExact',
1188
+ dt: XSD_DECIMAL_DT,
1189
+ decimalNum: parsed.num,
1190
+ decimalScale: parsed.scale,
1191
+ canonicalLex,
1192
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, XSD_DECIMAL_DT),
1193
+ comparable: true,
1194
+ };
1195
+ }
1196
+
1197
+ function isXsdFloatDoubleLexical(s) {
1198
+ if (parseXsdFloatSpecialLex(s) !== null) return true;
1199
+ return /^[+-]?(?:(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|\d+(?:[eE][+-]?\d+))$/.test(s);
1200
+ }
1201
+
1202
+ function parseFloatDoubleDatatypeValue(lexical, dt) {
1203
+ const s = String(lexical);
1204
+ if (!isXsdFloatDoubleLexical(s)) return null;
1205
+ const special = parseXsdFloatSpecialLex(s);
1206
+ const value = special !== null ? special : Number(s);
1207
+ if (Number.isNaN(value) && s !== 'NaN') return null;
1208
+ const canonicalLex = canonicalFloatDoubleLex(value);
1209
+ return {
1210
+ family: 'numeric',
1211
+ numericKind: 'floatDouble',
1212
+ dt,
1213
+ value,
1214
+ canonicalLex,
1215
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1216
+ comparable: !Number.isNaN(value),
1217
+ };
1218
+ }
1219
+
1220
+ function normalizeBase64Lexical(s) {
1221
+ return String(s).replace(/[\t\n\r ]+/g, '');
1222
+ }
1223
+
1224
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
1225
+ const BASE64_LOOKUP = (() => {
1226
+ const m = Object.create(null);
1227
+ for (let i = 0; i < BASE64_ALPHABET.length; i++) m[BASE64_ALPHABET[i]] = i;
1228
+ return m;
1229
+ })();
1230
+
1231
+ function decodeBase64Bytes(input) {
1232
+ const s = normalizeBase64Lexical(input);
1233
+ if (s.length === 0) return [];
1234
+ if (s.length % 4 !== 0) return null;
1235
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(s)) return null;
1236
+ const pad = s.endsWith('==') ? 2 : s.endsWith('=') ? 1 : 0;
1237
+ if (pad && s.slice(0, -pad).includes('=')) return null;
1238
+
1239
+ const out = [];
1240
+ for (let i = 0; i < s.length; i += 4) {
1241
+ const c0 = BASE64_LOOKUP[s[i]];
1242
+ const c1 = BASE64_LOOKUP[s[i + 1]];
1243
+ const c2 = s[i + 2] === '=' ? 0 : BASE64_LOOKUP[s[i + 2]];
1244
+ const c3 = s[i + 3] === '=' ? 0 : BASE64_LOOKUP[s[i + 3]];
1245
+ if (c0 == null || c1 == null || c2 == null || c3 == null) return null;
1246
+ const n = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
1247
+ out.push((n >> 16) & 0xff);
1248
+ if (s[i + 2] !== '=') out.push((n >> 8) & 0xff);
1249
+ if (s[i + 3] !== '=') out.push(n & 0xff);
1250
+ if ((s[i + 2] === '=' || s[i + 3] === '=') && i + 4 !== s.length) return null;
1251
+ }
1252
+ return out;
1253
+ }
1254
+
1255
+ function encodeBase64Bytes(bytes) {
1256
+ let out = '';
1257
+ for (let i = 0; i < bytes.length; i += 3) {
1258
+ const b0 = bytes[i];
1259
+ const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
1260
+ const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
1261
+ const n = (b0 << 16) | (b1 << 8) | b2;
1262
+ out += BASE64_ALPHABET[(n >> 18) & 63];
1263
+ out += BASE64_ALPHABET[(n >> 12) & 63];
1264
+ out += i + 1 < bytes.length ? BASE64_ALPHABET[(n >> 6) & 63] : '=';
1265
+ out += i + 2 < bytes.length ? BASE64_ALPHABET[n & 63] : '=';
1266
+ }
1267
+ return out;
1268
+ }
1269
+
1270
+ function parseBinaryDatatypeValue(lexical, dt) {
1271
+ const s = String(lexical);
1272
+ let bytes;
1273
+ let canonicalLex;
1274
+ if (dt === XSD_HEX_BINARY_DT) {
1275
+ if (!/^(?:[0-9A-Fa-f]{2})*$/.test(s)) return null;
1276
+ bytes = [];
1277
+ for (let i = 0; i < s.length; i += 2) bytes.push(parseInt(s.slice(i, i + 2), 16));
1278
+ canonicalLex = s.toUpperCase();
1279
+ } else {
1280
+ bytes = decodeBase64Bytes(s);
1281
+ if (bytes === null) return null;
1282
+ canonicalLex = encodeBase64Bytes(bytes);
1283
+ }
1284
+
1285
+ return {
1286
+ family: 'binary',
1287
+ dt,
1288
+ bytes,
1289
+ canonicalLex,
1290
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1291
+ comparable: true,
1292
+ };
1293
+ }
1294
+
1295
+ function parseAnyUriDatatypeValue(lexical) {
1296
+ const value = String(lexical);
1297
+ if (/[\u0000-\u001F\u007F]/.test(value)) return null;
1298
+ return {
1299
+ family: 'anyURI',
1300
+ dt: XSD_ANY_URI_DT,
1301
+ value,
1302
+ canonicalLex: value,
1303
+ canonicalLiteral: makeDatatypeTypedLiteral(value, XSD_ANY_URI_DT),
1304
+ comparable: true,
1305
+ };
1306
+ }
1307
+
1308
+ function isLeapYearNumber(year) {
1309
+ if (year % 400 === 0) return true;
1310
+ if (year % 100 === 0) return false;
1311
+ return year % 4 === 0;
1312
+ }
1313
+
1314
+ function daysInMonthNumber(year, month) {
1315
+ if (month === 2) return isLeapYearNumber(year) ? 29 : 28;
1316
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
1317
+ }
1318
+
1319
+ function floorDivNumber(a, b) {
1320
+ return Math.floor(a / b);
1321
+ }
1322
+
1323
+ function daysFromCivilNumber(year, month, day) {
1324
+ let y = year;
1325
+ y -= month <= 2 ? 1 : 0;
1326
+ const era = floorDivNumber(y, 400);
1327
+ const yoe = y - era * 400;
1328
+ const mp = month + (month > 2 ? -3 : 9);
1329
+ const doy = Math.floor((153 * mp + 2) / 5) + day - 1;
1330
+ const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
1331
+ return era * 146097 + doe - 719468;
1332
+ }
1333
+
1334
+ function parseDateTimeDatatypeValue(lexical, dt) {
1335
+ const s = String(lexical);
1336
+ const m = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})?$/.exec(s);
1337
+ if (!m) return null;
1338
+
1339
+ const year = Number(m[1]);
1340
+ const month = Number(m[2]);
1341
+ let day = Number(m[3]);
1342
+ let hour = Number(m[4]);
1343
+ const minute = Number(m[5]);
1344
+ const second = Number(m[6]);
1345
+ let frac = m[7] || '';
1346
+ const tz = m[8] || null;
1347
+ if (!Number.isSafeInteger(year)) return null;
1348
+ if (dt === XSD_DATETIME_STAMP_DT && !tz) return null;
1349
+ if (!(month >= 1 && month <= 12)) return null;
1350
+ if (!(day >= 1 && day <= daysInMonthNumber(year, month))) return null;
1351
+ if (!(minute >= 0 && minute <= 59)) return null;
1352
+ if (!(second >= 0 && second <= 59)) return null;
1353
+ if (hour === 24) {
1354
+ if (minute !== 0 || second !== 0 || /[1-9]/.test(frac)) return null;
1355
+ hour = 0;
1356
+ const d = new Date(Date.UTC(year, month - 1, day));
1357
+ d.setUTCFullYear(year);
1358
+ d.setUTCDate(d.getUTCDate() + 1);
1359
+ day = d.getUTCDate();
1360
+ } else if (!(hour >= 0 && hour <= 23)) {
1361
+ return null;
1362
+ }
1363
+
1364
+ frac = frac.replace(/0+$/g, '');
1365
+ const fracScale = frac.length;
1366
+ const fracNum = frac ? BigInt(frac) : 0n;
1367
+
1368
+ let offsetMinutes = null;
1369
+ if (tz) {
1370
+ if (tz === 'Z') {
1371
+ offsetMinutes = 0;
1372
+ } else {
1373
+ const sign = tz[0] === '-' ? -1 : 1;
1374
+ const hh = Number(tz.slice(1, 3));
1375
+ const mm = Number(tz.slice(4, 6));
1376
+ if (!(hh >= 0 && hh <= 14) || !(mm >= 0 && mm <= 59)) return null;
1377
+ if (hh === 14 && mm !== 0) return null;
1378
+ offsetMinutes = sign * (hh * 60 + mm);
1379
+ }
1380
+ }
1381
+
1382
+ const dayCount = daysFromCivilNumber(year, month, day);
1383
+ let totalSeconds = BigInt(dayCount) * 86400n + BigInt(hour * 3600 + minute * 60 + second);
1384
+ if (offsetMinutes !== null) totalSeconds -= BigInt(offsetMinutes * 60);
1385
+
1386
+ const localKey = `${m[1]}-${m[2]}-${String(day).padStart(2, '0')}T${String(hour).padStart(2, '0')}:${m[5]}:${m[6]}${frac ? `.${frac}` : ''}`;
1387
+ const canonicalLex = canonicalDateTimeLex(totalSeconds, fracNum, fracScale, offsetMinutes !== null, localKey);
1388
+ return {
1389
+ family: 'dateTime',
1390
+ dt,
1391
+ hasTimezone: offsetMinutes !== null,
1392
+ totalSeconds,
1393
+ fracNum,
1394
+ fracScale,
1395
+ localKey,
1396
+ canonicalLex,
1397
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1398
+ comparable: true,
1399
+ };
1400
+ }
1401
+
1402
+ function canonicalDateTimeLex(totalSeconds, fracNum, fracScale, hasTimezone, localKey) {
1403
+ const frac = fracScale > 0 ? `.${fracNum.toString().padStart(fracScale, '0')}` : '';
1404
+ if (!hasTimezone) return localKey;
1405
+
1406
+ const minSafeSeconds = BigInt(Math.ceil(Number.MIN_SAFE_INTEGER / 1000));
1407
+ const maxSafeSeconds = BigInt(Math.floor(Number.MAX_SAFE_INTEGER / 1000));
1408
+ if (totalSeconds < minSafeSeconds || totalSeconds > maxSafeSeconds) return `${localKey}Z`;
1409
+
1410
+ const d = new Date(Number(totalSeconds) * 1000);
1411
+ if (Number.isNaN(d.getTime())) return `${localKey}Z`;
1412
+ let iso = d.toISOString().replace(/\.000Z$/, 'Z');
1413
+ if (frac) iso = iso.replace('Z', `${frac}Z`);
1414
+ return iso;
1415
+ }
1416
+
1417
+ function parseLangStringDatatypeValue(t, lexical) {
1418
+ const tag = extractLiteralLanguageTag(t.value);
1419
+ if (tag === null) return null;
1420
+ return {
1421
+ family: 'langString',
1422
+ dt: RDF_LANGSTRING_DT,
1423
+ value: String(lexical),
1424
+ lang: tag.toLowerCase(),
1425
+ canonicalLex: String(lexical),
1426
+ canonicalLiteral: makeLangStringLiteral(lexical, tag),
1427
+ comparable: true,
1428
+ };
1429
+ }
1430
+
1431
+ function parseDatatypeValueForDatatype(t, dt) {
1432
+ if (!(t instanceof Literal) || typeof dt !== 'string' || !isSupportedDatatypeIri(dt)) return null;
1433
+ const lexical = literalLexicalValue(t.value);
1434
+
1435
+ if (dt === RDF_LANGSTRING_DT) return parseLangStringDatatypeValue(t, lexical);
1436
+ if (literalHasLangTag(t.value)) return null;
1437
+ if (XSD_STRING_LIKE_DTS.has(dt)) return parseStringLikeDatatypeValue(lexical, dt);
1438
+ if (dt === XSD_BOOLEAN_DT) return parseBooleanDatatypeValue(lexical);
1439
+ if (dt === XSD_INTEGER_DT || XSD_INTEGER_BOUNDS.has(dt)) return parseIntegerDatatypeValue(lexical, dt);
1440
+ if (dt === XSD_DECIMAL_DT) return parseDecimalDatatypeValue(lexical);
1441
+ if (dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) return parseFloatDoubleDatatypeValue(lexical, dt);
1442
+ if (dt === XSD_HEX_BINARY_DT || dt === XSD_BASE64_BINARY_DT) return parseBinaryDatatypeValue(lexical, dt);
1443
+ if (dt === XSD_ANY_URI_DT) return parseAnyUriDatatypeValue(lexical);
1444
+ if (dt === XSD_DATETIME_DT || dt === XSD_DATETIME_STAMP_DT) return parseDateTimeDatatypeValue(lexical, dt);
1445
+
1446
+ return null;
1447
+ }
1448
+
1449
+ function parseDatatypeValue(t) {
1450
+ const dt = literalDatatypeIri(t);
1451
+ return dt === null ? null : parseDatatypeValueForDatatype(t, dt);
1452
+ }
1453
+
1454
+ function compareDecimalExactValues(a, b) {
1455
+ const scale = Math.max(a.decimalScale, b.decimalScale);
1456
+ return a.decimalNum * pow10n(scale - a.decimalScale) === b.decimalNum * pow10n(scale - b.decimalScale);
1457
+ }
1458
+
1459
+ function datatypeValuesSame(a, b) {
1460
+ if (!a || !b || !a.comparable || !b.comparable) return false;
1461
+ if (a.family === 'langString' || b.family === 'langString') {
1462
+ return a.family === b.family && a.value === b.value && a.lang === b.lang;
1463
+ }
1464
+ if (a.family === 'string' && b.family === 'string') return a.value === b.value;
1465
+ if (a.family === 'boolean' && b.family === 'boolean') return a.value === b.value;
1466
+ if (a.family === 'anyURI' && b.family === 'anyURI') return a.value === b.value;
1467
+ if (a.family === 'binary' && b.family === 'binary') {
1468
+ if (a.bytes.length !== b.bytes.length) return false;
1469
+ for (let i = 0; i < a.bytes.length; i++) if (a.bytes[i] !== b.bytes[i]) return false;
1470
+ return true;
1471
+ }
1472
+ if (a.family === 'numeric' && b.family === 'numeric') {
1473
+ if (a.numericKind === 'decimalExact' && b.numericKind === 'decimalExact') return compareDecimalExactValues(a, b);
1474
+ const av = a.numericKind === 'decimalExact' ? Number(a.decimalNum) / 10 ** a.decimalScale : a.value;
1475
+ const bv = b.numericKind === 'decimalExact' ? Number(b.decimalNum) / 10 ** b.decimalScale : b.value;
1476
+ return !Number.isNaN(av) && !Number.isNaN(bv) && av === bv;
1477
+ }
1478
+ if (a.family === 'dateTime' && b.family === 'dateTime') {
1479
+ if (a.hasTimezone !== b.hasTimezone) return false;
1480
+ if (!a.hasTimezone) return a.localKey === b.localKey;
1481
+ if (a.totalSeconds !== b.totalSeconds) return false;
1482
+ const scale = Math.max(a.fracScale, b.fracScale);
1483
+ return a.fracNum * pow10n(scale - a.fracScale) === b.fracNum * pow10n(scale - b.fracScale);
1484
+ }
1485
+ return false;
1486
+ }
1487
+
1488
+ function datatypeValuesComparable(a, b) {
1489
+ if (!a || !b || !a.comparable || !b.comparable) return false;
1490
+ if (a.family === 'dateTime' && b.family === 'dateTime' && a.hasTimezone !== b.hasTimezone) return false;
1491
+ if (a.family === 'langString' || b.family === 'langString') return a.family === b.family;
1492
+ if (a.family === 'string' || b.family === 'string') return a.family === b.family;
1493
+ return a.family === b.family;
1494
+ }
1495
+
1496
+ function evalBindBuiltinObject(outTerm, valueTerm, subst) {
1497
+ if (outTerm instanceof Var) {
1498
+ const s2 = __cloneSubst(subst);
1499
+ s2[outTerm.name] = valueTerm;
1500
+ return [s2];
1501
+ }
1502
+ if (outTerm instanceof Blank) return [__cloneSubst(subst)];
1503
+ const s2 = unifyTerm(outTerm, valueTerm, subst);
1504
+ return s2 !== null ? [s2] : [];
1505
+ }
1506
+
1507
+ function evalDatatypeInspectionBuiltin(g, subst, kind) {
1508
+ if (!(g.s instanceof Literal)) return [];
1509
+
1510
+ let valueTerm;
1511
+ if (kind === 'datatype') {
1512
+ const dt = literalDatatypeIri(g.s);
1513
+ if (dt === null) return [];
1514
+ valueTerm = internIri(dt);
1515
+ } else if (kind === 'lexicalForm') {
1516
+ valueTerm = makeStringLiteral(literalLexicalValue(g.s.value));
1517
+ } else if (kind === 'language') {
1518
+ const tag = extractLiteralLanguageTag(g.s.value);
1519
+ if (tag === null) return [];
1520
+ valueTerm = makeStringLiteral(tag);
1521
+ } else if (kind === 'canonicalLiteral') {
1522
+ const info = parseDatatypeValue(g.s);
1523
+ if (!info) return [];
1524
+ valueTerm = info.canonicalLiteral;
1525
+ } else {
1526
+ return [];
1527
+ }
1528
+
1529
+ return evalBindBuiltinObject(g.o, valueTerm, subst);
1530
+ }
1531
+
1532
+ function evalDatatypeValidityBuiltin(g, subst, positive) {
1533
+ if (!(g.s instanceof Literal)) return [];
1534
+
1535
+ let dt = null;
1536
+ if (g.o instanceof Iri) dt = g.o.value;
1537
+ else if (g.o instanceof Var) dt = literalDatatypeIri(g.s);
1538
+ else return [];
1539
+
1540
+ if (dt === null || !isSupportedDatatypeIri(dt)) return [];
1541
+ const ok = parseDatatypeValueForDatatype(g.s, dt) !== null;
1542
+ if (positive && !ok) return [];
1543
+ if (!positive && ok) return [];
1544
+
1545
+ if (g.o instanceof Var) return evalBindBuiltinObject(g.o, internIri(dt), subst);
1546
+ return [__cloneSubst(subst)];
1547
+ }
1548
+
1549
+ function evalDatatypeValueComparisonBuiltin(g, subst, same) {
1550
+ if (!(g.s instanceof Literal) || !(g.o instanceof Literal)) return [];
1551
+ const a = parseDatatypeValue(g.s);
1552
+ const b = parseDatatypeValue(g.o);
1553
+ if (!datatypeValuesComparable(a, b)) return [];
1554
+ const eq = datatypeValuesSame(a, b);
1555
+ if (same ? eq : !eq) return [__cloneSubst(subst)];
1556
+ return [];
1557
+ }
1558
+
970
1559
  // ===========================================================================
971
1560
  // Math builtin helpers
972
1561
  // ===========================================================================
@@ -2115,6 +2704,19 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2115
2704
  const registeredBuiltinResult = __evalRegisteredBuiltin(pv, g, subst, facts, backRules, depth, varGen, maxResults);
2116
2705
  if (registeredBuiltinResult !== null) return registeredBuiltinResult;
2117
2706
 
2707
+ // -----------------------------------------------------------------
2708
+ // 4.0 datatype: builtins (Eyeling datatype namespace)
2709
+ // -----------------------------------------------------------------
2710
+
2711
+ if (pv === DT_NS + 'datatype') return evalDatatypeInspectionBuiltin(g, subst, 'datatype');
2712
+ if (pv === DT_NS + 'lexicalForm') return evalDatatypeInspectionBuiltin(g, subst, 'lexicalForm');
2713
+ if (pv === DT_NS + 'language') return evalDatatypeInspectionBuiltin(g, subst, 'language');
2714
+ if (pv === DT_NS + 'canonicalLiteral') return evalDatatypeInspectionBuiltin(g, subst, 'canonicalLiteral');
2715
+ if (pv === DT_NS + 'validForDatatype') return evalDatatypeValidityBuiltin(g, subst, true);
2716
+ if (pv === DT_NS + 'invalidForDatatype') return evalDatatypeValidityBuiltin(g, subst, false);
2717
+ if (pv === DT_NS + 'sameValueAs') return evalDatatypeValueComparisonBuiltin(g, subst, true);
2718
+ if (pv === DT_NS + 'differentValueFrom') return evalDatatypeValueComparisonBuiltin(g, subst, false);
2719
+
2118
2720
  // -----------------------------------------------------------------
2119
2721
  // 4.1 crypto: builtins
2120
2722
  // -----------------------------------------------------------------
@@ -4440,6 +5042,7 @@ function isBuiltinPred(p) {
4440
5042
  v.startsWith(MATH_NS) ||
4441
5043
  v.startsWith(LOG_NS) ||
4442
5044
  v.startsWith(STRING_NS) ||
5045
+ v.startsWith(DT_NS) ||
4443
5046
  v.startsWith(TIME_NS) ||
4444
5047
  v.startsWith(LIST_NS)
4445
5048
  );
@@ -14204,6 +14807,7 @@ const TIME_NS = 'http://www.w3.org/2000/10/swap/time#';
14204
14807
  const LIST_NS = 'http://www.w3.org/2000/10/swap/list#';
14205
14808
  const LOG_NS = 'http://www.w3.org/2000/10/swap/log#';
14206
14809
  const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
14810
+ const DT_NS = 'https://eyereasoner.github.io/eyeling/datatype#';
14207
14811
  const SKOLEM_NS = 'https://eyereasoner.github.io/.well-known/genid/';
14208
14812
  const RDF_JSON_DT = RDF_NS + 'JSON';
14209
14813
 
@@ -14722,6 +15326,7 @@ class PrefixEnv {
14722
15326
  m['string'] = STRING_NS;
14723
15327
  m['list'] = LIST_NS;
14724
15328
  m['time'] = TIME_NS;
15329
+ m['dt'] = DT_NS;
14725
15330
  m['genid'] = SKOLEM_NS;
14726
15331
  m[''] = ''; // empty prefix default namespace
14727
15332
  return new PrefixEnv(m, ''); // base IRI starts empty
@@ -14906,6 +15511,7 @@ module.exports = {
14906
15511
  LIST_NS,
14907
15512
  LOG_NS,
14908
15513
  STRING_NS,
15514
+ DT_NS,
14909
15515
  SKOLEM_NS,
14910
15516
  RDF_JSON_DT,
14911
15517
  resolveIriRef,