109 lines
2.6 KiB
Text
109 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’])
|