110 lines
2.6 KiB
Text
110 lines
2.6 KiB
Text
|
|
T.enum Kind
|
|||
|
|
STRING
|
|||
|
|
NUMBER
|
|||
|
|
|
|||
|
|
T KeyItem
|
|||
|
|
Kind kind
|
|||
|
|
String s
|
|||
|
|
Int num
|
|||
|
|
|
|||
|
|
F natOrderKey(=s)
|
|||
|
|
// Remove leading and trailing white spaces.
|
|||
|
|
s = s.trim((‘ ’, "\t", "\r", "\n"))
|
|||
|
|
|
|||
|
|
// Make all whitespace characters equivalent and remove adjacent spaces.
|
|||
|
|
s = s.replace(re:‘\s+’, ‘ ’)
|
|||
|
|
|
|||
|
|
// Switch to lower case.
|
|||
|
|
s = s.lowercase()
|
|||
|
|
|
|||
|
|
// Remove leading "the ".
|
|||
|
|
I s.starts_with(‘the ’) & s.len > 4
|
|||
|
|
s = s[4..]
|
|||
|
|
|
|||
|
|
// Split into fields.
|
|||
|
|
[KeyItem] result
|
|||
|
|
V idx = 0
|
|||
|
|
L idx < s.len
|
|||
|
|
V e = idx
|
|||
|
|
L e < s.len & !s[e].is_digit()
|
|||
|
|
e++
|
|||
|
|
I e > idx
|
|||
|
|
V ki = KeyItem()
|
|||
|
|
ki.kind = Kind.STRING
|
|||
|
|
ki.s = s[idx .< e]
|
|||
|
|
result.append(ki)
|
|||
|
|
idx = e
|
|||
|
|
L e < s.len & s[e].is_digit()
|
|||
|
|
e++
|
|||
|
|
I e > idx
|
|||
|
|
V ki = KeyItem()
|
|||
|
|
ki.kind = Kind.NUMBER
|
|||
|
|
ki.num = Int(s[idx .< e])
|
|||
|
|
result.append(ki)
|
|||
|
|
idx = e
|
|||
|
|
R result
|
|||
|
|
|
|||
|
|
F scmp(s1, s2)
|
|||
|
|
I s1 < s2 {R -1}
|
|||
|
|
I s1 > s2 {R 1}
|
|||
|
|
R 0
|
|||
|
|
|
|||
|
|
F naturalCmp(String sa, String sb)
|
|||
|
|
V a = natOrderKey(sa)
|
|||
|
|
V b = natOrderKey(sb)
|
|||
|
|
|
|||
|
|
L(i) 0 .< min(a.len, b.len)
|
|||
|
|
V ai = a[i]
|
|||
|
|
V bi = b[i]
|
|||
|
|
I ai.kind == bi.kind
|
|||
|
|
V result = I ai.kind == STRING {scmp(ai.s, bi.s)} E ai.num - bi.num
|
|||
|
|
I result != 0
|
|||
|
|
R result
|
|||
|
|
E
|
|||
|
|
R I ai.kind == STRING {1} E -1
|
|||
|
|
|
|||
|
|
R I a.len < b.len {-1} E (I a.len == b.len {0} E 1)
|
|||
|
|
|
|||
|
|
F test(title, list)
|
|||
|
|
print(title)
|
|||
|
|
print(sorted(list, key' cmp_to_key(naturalCmp)).map(s -> ‘'’s‘'’).join("\n"))
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
test(‘Ignoring leading spaces.’,
|
|||
|
|
[‘ignore leading spaces: 2-2’,
|
|||
|
|
‘ ignore leading spaces: 2-1’,
|
|||
|
|
‘ ignore leading spaces: 2+0’,
|
|||
|
|
‘ ignore leading spaces: 2+1’])
|
|||
|
|
|
|||
|
|
test(‘Ignoring multiple adjacent spaces (MAS).’,
|
|||
|
|
[‘ignore MAS spaces: 2-2’,
|
|||
|
|
‘ignore MAS spaces: 2-1’,
|
|||
|
|
‘ignore MAS spaces: 2+0’,
|
|||
|
|
‘ignore MAS spaces: 2+1’])
|
|||
|
|
|
|||
|
|
test(‘Equivalent whitespace characters.’,
|
|||
|
|
[‘Equiv. spaces: 3-3’,
|
|||
|
|
"Equiv. \rspaces: 3-2",
|
|||
|
|
"Equiv. \x0cspaces: 3-1",
|
|||
|
|
"Equiv. \x0bspaces: 3+0",
|
|||
|
|
"Equiv. \nspaces: 3+1",
|
|||
|
|
"Equiv. \tspaces: 3+2"])
|
|||
|
|
|
|||
|
|
test(‘Case Independent sort.’,
|
|||
|
|
[‘cASE INDEPENDENT: 3-2’,
|
|||
|
|
‘caSE INDEPENDENT: 3-1’,
|
|||
|
|
‘casE INDEPENDENT: 3+0’,
|
|||
|
|
‘case INDEPENDENT: 3+1’])
|
|||
|
|
|
|||
|
|
test(‘Numeric fields as numerics.’,
|
|||
|
|
[‘foo100bar99baz0.txt’,
|
|||
|
|
‘foo100bar10baz0.txt’,
|
|||
|
|
‘foo1000bar99baz10.txt’,
|
|||
|
|
‘foo1000bar99baz9.txt’])
|
|||
|
|
|
|||
|
|
test(‘Title sorts.’,
|
|||
|
|
[‘The Wind in the Willows’,
|
|||
|
|
‘The 40th step more’,
|
|||
|
|
‘The 39 steps’,
|
|||
|
|
‘Wanda’])
|