fnNormalizeSpace in src/expressions/functions/builtInFunctions_string.ts:390
trims with String.prototype.trim() and collapses with /\s+/g. Both read
JavaScript's whitespace class. XPath 3.1 F&O 5.4.2 defines fn:normalize-space
over the four characters of XML's S production, #x9, #xA, #xD and
#x20, so a no-break space, an em space, a line separator or a BOM is an
ordinary character to this function and must survive it untouched.
const stringValue = arg.first().value.trim();
return sequenceFactory.singleton(
createAtomicValue(stringValue.replace(/\s+/g, ' '), ValueType.XSSTRING),
);
Asking 3.34.0 for normalize-space("a" || $c || "b"), one character at a time:
character normalize-space tokenize#1
NO-BREAK SPACE U+00A0 "a b" two tokens
LINE SEPARATOR U+2028 "a b" two tokens
EM SPACE U+2003 "a b" two tokens
VERTICAL TAB U+000B "a b" two tokens
ZWNBSP U+FEFF "a b" two tokens
NEL U+0085 unchanged one token
Only the last row is right. NEL is the tell: JavaScript's \s is the one
whitespace class that excludes U+0085, and so does this implementation, which
fingerprints the class exactly rather than leaving the cause to guesswork.
The second column is fn:tokenize#1, which inherits the defect. The arity-one
form at line 972 is defined as fn:tokenize(fn:normalize-space($input), ' '),
exactly as the specification words it, so it splits on the wider class too and
answers two tokens where XPath says one. Nothing in the suite notices either
way: the normalize-space() block at
test/specs/parsing/functions/functions.string.tests.ts:105 exercises spaces
and tabs alone, which is the subset on which the two classes agree.
The correct class is already spelled twice in this repository, as WHITESPACE
in src/parsing/tokens.ts:3 and as the attribute-value normalization at
src/parsing/prscParser.ts:833, so the fix is to use it here as well: collapse
with /[\x20\x09\x0D\x0A]+/g and cut the ends with the anchored form of the
same class, instead of trim() and /\s+/g. One more case in that describe
block, holding a U+00A0 between two letters, keeps it from coming back.
fnNormalizeSpaceinsrc/expressions/functions/builtInFunctions_string.ts:390trims with
String.prototype.trim()and collapses with/\s+/g. Both readJavaScript's whitespace class. XPath 3.1 F&O 5.4.2 defines
fn:normalize-spaceover the four characters of XML's
Sproduction,#x9,#xA,#xDand#x20, so a no-break space, an em space, a line separator or a BOM is anordinary character to this function and must survive it untouched.
Asking 3.34.0 for
normalize-space("a" || $c || "b"), one character at a time:Only the last row is right. NEL is the tell: JavaScript's
\sis the onewhitespace class that excludes U+0085, and so does this implementation, which
fingerprints the class exactly rather than leaving the cause to guesswork.
The second column is
fn:tokenize#1, which inherits the defect. The arity-oneform at line 972 is defined as
fn:tokenize(fn:normalize-space($input), ' '),exactly as the specification words it, so it splits on the wider class too and
answers two tokens where XPath says one. Nothing in the suite notices either
way: the
normalize-space()block attest/specs/parsing/functions/functions.string.tests.ts:105exercises spacesand tabs alone, which is the subset on which the two classes agree.
The correct class is already spelled twice in this repository, as
WHITESPACEin
src/parsing/tokens.ts:3and as the attribute-value normalization atsrc/parsing/prscParser.ts:833, so the fix is to use it here as well: collapsewith
/[\x20\x09\x0D\x0A]+/gand cut the ends with the anchored form of thesame class, instead of
trim()and/\s+/g. One more case in that describeblock, holding a U+00A0 between two letters, keeps it from coming back.