Use an ASCII separator: ExprTK string literals are byte-wise isprint
"01 · Prior Year" would not parse -- Invalid `expressions` "Bucket": Invalid
string token: 01. ExprTK's string scanner validates each *byte*:
is_valid_string_char(c) = isprint((unsigned char) c) || is_whitespace(c)
"·" is U+00B7, two bytes 0xC2 0xB7 in UTF-8, and isprint(0xC2) is false in
the C locale. The literal fails at that byte and the parser reports from the
start of it, which is why the message names "01" rather than the character
it actually objected to.
" - " instead, which matches the "07 - Dec" already in the source data rather
than introducing a second convention.
The same rule applies to the label being compared, so a bucket or segment
named with anything outside printable ASCII is left unordered rather than
emitted into an expression that will not parse -- an unparseable expression
loses the whole column, not just that one case.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
916229bdab
commit
8c1f5b8f60
@ -426,7 +426,7 @@ export default function Baseline({ sources = [], sourceId, versions = [], versio
|
|||||||
<span key={b}
|
<span key={b}
|
||||||
className="inline-flex items-center gap-1 border border-gray-200 rounded
|
className="inline-flex items-center gap-1 border border-gray-200 rounded
|
||||||
pl-2 pr-1 py-0.5 text-xs bg-white">
|
pl-2 pr-1 py-0.5 text-xs bg-white">
|
||||||
<span className="text-gray-400 tabular-nums">{String(i + 1).padStart(2, '0')}</span>
|
<span className="text-gray-400 tabular-nums">{String(i + 1).padStart(2, '0')} -</span>
|
||||||
<span className="text-gray-700">{b}</span>
|
<span className="text-gray-700">{b}</span>
|
||||||
<button
|
<button
|
||||||
disabled={i === 0}
|
disabled={i === 0}
|
||||||
|
|||||||
@ -50,8 +50,27 @@ function cleanLayout(cfg, validCols) {
|
|||||||
// parts, and the mapping is known here anyway.
|
// parts, and the mapping is known here anyway.
|
||||||
const ORDER_EXPR_NAMES = { bucket: 'Bucket', segment: 'Segment' }
|
const ORDER_EXPR_NAMES = { bucket: 'Bucket', segment: 'Segment' }
|
||||||
|
|
||||||
|
// ASCII only, and " - " specifically because the source data already reads
|
||||||
|
// "07 - Dec". A middle dot was the first choice and ExprTK rejects it: its string
|
||||||
|
// scanner tests each *byte* with
|
||||||
|
//
|
||||||
|
// is_valid_string_char(c) = isprint((unsigned char) c) || is_whitespace(c)
|
||||||
|
//
|
||||||
|
// and "·" is U+00B7, two bytes 0xC2 0xB7 in UTF-8, of which isprint(0xC2) is
|
||||||
|
// false in the C locale. The literal fails at that byte and the parser reports
|
||||||
|
// from the start of it — "Invalid string token: 01".
|
||||||
|
const ORDER_SEP = ' - '
|
||||||
|
|
||||||
function orderedLabel(ord, label) {
|
function orderedLabel(ord, label) {
|
||||||
return `${String(ord).padStart(2, '0')} · ${label}`
|
return `${String(ord).padStart(2, '0')}${ORDER_SEP}${label}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same byte rule applies to the label being matched, so a bucket named with
|
||||||
|
// anything outside printable ASCII cannot appear in an expression at all. Such a
|
||||||
|
// label is left unordered rather than emitted into an expression that will not
|
||||||
|
// parse and take the whole column with it.
|
||||||
|
function isExprSafe(v) {
|
||||||
|
return /^[\x20-\x7e]*$/.test(String(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
function sqlSafe(v) {
|
function sqlSafe(v) {
|
||||||
@ -63,7 +82,7 @@ function sqlSafe(v) {
|
|||||||
// than silently landing first.
|
// than silently landing first.
|
||||||
function buildOrderExpression(sourceCol, pairs) {
|
function buildOrderExpression(sourceCol, pairs) {
|
||||||
const cases = pairs
|
const cases = pairs
|
||||||
.filter(([label, ord]) => label && ord > 0)
|
.filter(([label, ord]) => label && ord > 0 && isExprSafe(label))
|
||||||
.sort((a, b) => a[1] - b[1])
|
.sort((a, b) => a[1] - b[1])
|
||||||
.map(([label, ord]) =>
|
.map(([label, ord]) =>
|
||||||
`if ("${sourceCol}" == '${sqlSafe(label)}') { '${sqlSafe(orderedLabel(ord, label))}' }`)
|
`if ("${sourceCol}" == '${sqlSafe(label)}') { '${sqlSafe(orderedLabel(ord, label))}' }`)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user