UNPKG

@csvw-rdf-convertor/core

Version:

This library was generated with [Nx](https://nx.dev).

337 lines (336 loc) 12.4 kB
import { commonPrefixes, invalidValuePrefix } from './prefix.js'; const { xsd } = commonPrefixes; const numericRegexes = { [xsd + 'integer']: /^[-+]?[0-9]+$/, [xsd + 'decimal']: /^(\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)$/, [xsd + 'long']: /^[-+]?[0-9]+$/, [xsd + 'int']: /^[-+]?[0-9]+$/, [xsd + 'short']: /^[-+]?[0-9]+$/, [xsd + 'byte']: /^[-+]?[0-9]+$/, [xsd + 'nonNegativeInteger']: /^\+?[0-9]+$/, [xsd + 'positiveInteger']: /^\+?[1-9][0-9]*$/, [xsd + 'unsignedLong']: /^[-+]?[0-9]+$/, [xsd + 'unsignedInt']: /^[-+]?[0-9]+$/, [xsd + 'unsignedShort']: /^[-+]?[0-9]+$/, [xsd + 'unsignedByte']: /^[-+]?[0-9]+$/, [xsd + 'double']: /^(\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)([Ee](\+|-)?[0-9]+)?$/, [xsd + 'float']: /^(\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)([Ee](\+|-)?[0-9]+)?$/, [xsd + 'nonPositiveInteger']: /^(0|(-[1-9][0-9]*))$/, [xsd + 'negativeInteger']: /^-[1-9][0-9]*$/ }; const extents = { [xsd + 'long']: [ -(2 ** 63), 2 ** 63 - 1 ], [xsd + 'int']: [ -(2 ** 31), 2 ** 31 - 1 ], [xsd + 'short']: [ -(2 ** 15), 2 ** 15 - 1 ], [xsd + 'byte']: [ -(2 ** 7), 2 ** 7 - 1 ], [xsd + 'unsignedLong']: [ 0, 2 ** 64 - 1 ], [xsd + 'unsignedInt']: [ 0, 2 ** 32 - 1 ], [xsd + 'unsignedShort']: [ 0, 2 ** 16 - 1 ], [xsd + 'unsignedByte']: [ 0, 2 ** 8 - 1 ] }; /** * Numbers in csv can be formatted in many different ways. This class can parse them into a standard format. */ export class NumberParser { constraintToNumber(value) { if (typeof value === 'string') { if (value === 'INF') return Infinity; if (value === '-INF') return -Infinity; return +value; } return value != null ? value : undefined; } validateMinMax(value, dt) { var _dt_minimum; const minimum = this.constraintToNumber((_dt_minimum = dt.minimum) != null ? _dt_minimum : dt.minInclusive); var _dt_maximum; const maximum = this.constraintToNumber((_dt_maximum = dt.maximum) != null ? _dt_maximum : dt.maxInclusive); const minExclusive = this.constraintToNumber(dt.minExclusive); const maxExclusive = this.constraintToNumber(dt.maxExclusive); if (minimum !== undefined && value < minimum) { this.issueTracker.addWarning(`The value "${value}" does not meet the minimum "${minimum}".`); return false; } if (maximum !== undefined && value > maximum) { this.issueTracker.addWarning(`The value "${value}" does not meet the maximum "${maximum}".`); return false; } if (minExclusive !== undefined && value <= minExclusive) { this.issueTracker.addWarning(`The value "${value}" does not meet the exclusive minimum "${dt.minExclusive}".`); return false; } if (maxExclusive !== undefined && value >= maxExclusive) { this.issueTracker.addWarning(`The value "${value}" does not meet the exclusive minimum "${dt.minExclusive}".`); return false; } return true; } /** * Parse a number from a string using the given format. */ parse(value, format, dtUri, dt) { if ((dtUri === xsd + 'float' || dtUri === xsd + 'double') && (value === 'INF' || value === 'NaN' || value === '-INF')) { if (value === 'INF' && !this.validateMinMax(Infinity, dt) || value === '-INF' && !this.validateMinMax(-Infinity, dt)) { return invalidValuePrefix + value; } return value; } const decimal = format == null ? void 0 : format.decimalChar; const groupChar = format == null ? void 0 : format.groupChar; const pattern = format == null ? void 0 : format.pattern; const transformedNumber = pattern == null ? this.castToNumberWithoutPattern(value, decimal, groupChar, dtUri) : this.castToNumberByPattern(pattern, value, decimal, groupChar); if (transformedNumber == null) { if (pattern == null) { this.issueTracker.addWarning(`The value "${value}" does not match the datatype ${dtUri.slice(xsd.length)}.`); } else { this.issueTracker.addWarning(`The value "${value}" does not match the pattern "${pattern}".`); } return invalidValuePrefix + value; } if (!this.validateMinMax(transformedNumber, dt)) { return invalidValuePrefix + value; } if (transformedNumber === 0 && value.startsWith('-')) return '-0'; return transformedNumber + ''; } castToNumberWithoutPattern(value, decimal, groupChar, dtUri) { if (value.split(groupChar).some((v)=>v.length === 0)) return null; value = value.replaceAll(groupChar, '').replaceAll(decimal, '.'); let divideBy = 1; if (value.endsWith('%')) { divideBy = 100; value = value.slice(0, -1); } else if (value.endsWith('‰')) { divideBy = 1000; value = value.slice(0, -1); } else if (value.startsWith('%')) { divideBy = 100; value = value.slice(1); } else if (value.startsWith('‰')) { divideBy = 1000; value = value.slice(1); } if (!value.match(numericRegexes[dtUri])) { return null; } const ext = extents[dtUri]; const num = +value; if (ext) { if (num < ext[0] || num > ext[1]) { return null; } } return num / divideBy; } castToNumberByPattern(pattern, number, decimalChar, groupChar) { let divideBy; const getDivideBy = this.getDivideBy(pattern, number); if (getDivideBy == null) { return null; } ({ pattern, number, divideBy } = getDivideBy); if (pattern.includes('e') !== number.includes('e') || pattern.includes('E') !== number.includes('E')) { return null; } pattern = pattern.toLowerCase(); number = number.toLowerCase(); const [mantissaPattern, expPattern] = pattern.split('e'); const [integerPattern, decimalPattern] = mantissaPattern.split('.'); const [mantissa, exp] = number.split('e'); const [integer, decimal] = mantissa.split(decimalChar); let res = this.castInteger(integer, integerPattern != null ? integerPattern : '', groupChar); if (res === null) { return null; } if (decimal) { if (decimalPattern === undefined) return null; const decRes = this.castDecimals(decimal, decimalPattern, groupChar); if (decRes === null) { return null; } res += '.' + decRes; } else if (decimalPattern && decimalPattern.includes('0')) { return null; } if (exp) { const expRes = this.castInteger(exp, expPattern != null ? expPattern : '', groupChar); if (expRes === null) { return null; } res += 'e' + expRes; } return +res / divideBy; } getDivideBy(pattern, number) { for (const [char, val] of [ [ '%', 100 ], [ '‰', 1000 ] ]){ for (const pos of [ 'startsWith', 'endsWith' ]){ if (pattern[pos](char)) { if (!number[pos](char)) { return null; } const i = pos === 'startsWith' ? 1 : 0; return { pattern: pattern.slice(i, pattern.length - 1 + i), number: number.slice(i, number.length - 1 + i), divideBy: val }; } } } return { pattern: pattern, number: number, divideBy: 1 }; } castDecimals(decimals, pattern, groupChar) { pattern = this.expandDecimalGroupings(pattern, decimals.length); let res = ''; let pI = 0; for(let dI = 0; dI < decimals.length; dI++){ const p = pattern[pI]; const d = decimals[dI]; if (p === '0') { if (!this.isNumberChar(d)) { return null; } res += d; pI++; } else if (p === '#') { if (this.isNumberChar(d)) { res += d; pI++; } else { while(pI < pattern.length && (pattern[pI] === '#' || pattern[pI] === ','))++pI; dI--; } } else if (p === ',') { if (d !== groupChar) { return null; } pI++; } else { return null; } } if (pI < pattern.length && pattern.slice(pI).replace(/[#,]/g, '').length !== 0) return null; return res; } castInteger(integer, pattern, groupChar) { let res = ''; let sign = ''; if (pattern[0] === '+' || pattern[0] === '-') { pattern = pattern.slice(1); if (integer[0] !== '-' && integer[0] !== '+') { return null; } } if (integer[0] === '-' || integer[0] === '+') { sign = integer[0]; integer = integer.slice(1); } pattern = this.expandIntGroupings(pattern, integer.length); let pI = pattern.length - 1; for(let iI = integer.length - 1; iI >= 0; --iI){ const p = pattern[pI]; const d = integer[iI]; if (p === '0') { if (!this.isNumberChar(d)) { return null; } res = d + res; pI--; } else if (p === '#') { if (this.isNumberChar(d)) { res = d + res; pI--; } else { while(pI >= 0 && (pattern[pI] === '#' || pattern[pI] === ','))pI--; iI++; } } else if (p === ',') { if (d !== groupChar) { return null; } pI--; } else if (p === undefined && this.isNumberChar(d)) { if (integer.startsWith('0')) return null; res = d + res; } else if (p === undefined && d === groupChar) { return null; } else { return null; } } if (pI >= 0 && pattern.slice(0, pI + 1).replace(/[#,]/g, '').length !== 0) { return null; } return sign + res; } isNumberChar(char) { return char >= '0' && char <= '9'; } expandIntGroupings(pattern, toSize) { const tokens = pattern.split(','); if (tokens.length === 1) return pattern; const correctGroups = tokens.length > 2 ? 2 : 1; const correct = tokens.slice(-correctGroups); const groupSize = correct[0].length; const first = tokens.slice(0, -correctGroups).join('').padStart(toSize, '#'); // the length is slight overkill for(let i = 0; i < first.length; i += groupSize){ correct.unshift(first.slice(Math.max(first.length - i - groupSize, 0), first.length - i)); } return correct.join(','); } expandDecimalGroupings(pattern, toSize) { const [first, ...rest] = pattern.split(','); if (rest.length === 0) return pattern; const correct = [ first ]; const groupSize = first.length; const last = rest.join('').padEnd(toSize, '#'); // the length is slight overkill for(let i = 0; i < last.length; i += groupSize){ correct.push(last.slice(i, i + groupSize)); } return correct.join(','); } constructor(issueTracker){ this.issueTracker = issueTracker; } } //# sourceMappingURL=parse-number.js.map