Skip to content

Commit f5dba78

Browse files
committed
feat: port derived age
1 parent 9c0ecf7 commit f5dba78

4 files changed

Lines changed: 97 additions & 3 deletions

File tree

UnicodeData.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public import UnicodeData.DerivedCombiningClass
1111
public import UnicodeData.DerivedBinaryProperties
1212
public import UnicodeData.DerivedGeneralCategory
1313
public import UnicodeData.DerivedLineBreak
14+
public import UnicodeData.DerivedAge
1415
public import UnicodeData.BidiBrackets
1516
public import UnicodeData.BidiMirroring
1617
public import UnicodeData.Blocks

UnicodeData/DerivedAge.lean

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/-
2+
Copyright © 2026 François G. Dorais. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
-/
5+
module
6+
public import UnicodeBasic.Types
7+
import UnicodeBasic.CharacterDatabase
8+
9+
namespace Unicode
10+
11+
/-- Raw string from `DerivedAge.txt` -/
12+
def DerivedAge.txt := include_str "../data/ucd/core/DerivedAge.txt"
13+
14+
/-- Trim ASCII whitespace from a string and materialize a `String`. -/
15+
private def trimAsciiString (s : String) : String := (String.trimAscii s).copy
16+
17+
/-- Trim ASCII whitespace from a string slice and materialize a `String`. -/
18+
private def trimAsciiSlice (s : String.Slice) : String := (String.Slice.trimAscii s).copy
19+
20+
/-- Strip trailing comments from a UCD line. -/
21+
private def stripComment (s : String) : String :=
22+
trimAsciiSlice <| String.takeWhile s fun c => c != '#'
23+
24+
/-- Parse a hex range field. -/
25+
private def parseRangeField (s : String) : UInt32 × UInt32 :=
26+
match (trimAsciiString s).splitOn ".." with
27+
| [c] => (ofHexString! c, ofHexString! c)
28+
| [c₀, c₁] => (ofHexString! c₀, ofHexString! c₁)
29+
| _ => panic! "invalid range in DerivedAge.txt"
30+
31+
/-- Parsed `DerivedAge.txt` records and defaults. -/
32+
public def DerivedAge.data : Array (UInt32 × UInt32 × String) := Id.run do
33+
let mut explicit := #[]
34+
let mut defaults := #[]
35+
for raw in DerivedAge.txt.splitOn "\n" do
36+
let line := trimAsciiString raw
37+
if line.isEmpty || line.startsWith "# " then
38+
if line.startsWith "# @missing:" then
39+
let body := trimAsciiSlice <| line.drop "# @missing:".length
40+
let parts := body.splitOn ";"
41+
let (c₀, c₁) := parseRangeField parts[0]!
42+
let age := trimAsciiSlice parts[1]!
43+
defaults := defaults.push (c₀, c₁, age)
44+
else
45+
continue
46+
else
47+
let line := stripComment line
48+
if line.isEmpty then
49+
continue
50+
let parts := line.splitOn ";"
51+
let (c₀, c₁) := parseRangeField parts[0]!
52+
let age := trimAsciiSlice parts[1]!
53+
explicit := explicit.push (c₀, c₁, age)
54+
return explicit.qsort fun a b => a.1.1 < b.1.1
55+
56+
private def DerivedAge.defaults : Array (UInt32 × UInt32 × String) := Id.run do
57+
let mut defaults := #[]
58+
for raw in DerivedAge.txt.splitOn "\n" do
59+
let line := trimAsciiString raw
60+
if line.startsWith "# @missing:" then
61+
let body := trimAsciiSlice <| line.drop "# @missing:".length
62+
let parts := body.splitOn ";"
63+
let (c₀, c₁) := parseRangeField parts[0]!
64+
let age := trimAsciiSlice parts[1]!
65+
defaults := defaults.push (c₀, c₁, age)
66+
return defaults.qsort fun a b => a.1.1 < b.1.1
67+
68+
private partial def find (code : UInt32)
69+
(data : Array (UInt32 × UInt32 × String)) (lo hi : Nat) : Option String :=
70+
if _ : lo < hi then
71+
let mid := lo + (hi - lo) / 2
72+
let (c₀, c₁, age) := data[mid]!
73+
if code < c₀ then
74+
find code data lo mid
75+
else if c₁ < code then
76+
find code data (mid + 1) hi
77+
else
78+
some age
79+
else
80+
none
81+
82+
/-- Find the age for a code point, if explicitly listed. -/
83+
public def lookupDerivedAge? (code : UInt32) : Option String :=
84+
match find code DerivedAge.data 0 DerivedAge.data.size with
85+
| some age => some age
86+
| none => find code DerivedAge.defaults 0 DerivedAge.defaults.size
87+
88+
/-- Find the age for a code point, defaulting to `NA`. -/
89+
public def lookupDerivedAge (code : UInt32) : String :=
90+
lookupDerivedAge? code |>.getD "NA"
91+
92+
end Unicode

docs/status/ucd-txt-usage.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ Generated from a repo scan of `data/ucd/**/*.txt` against Lean library files in
55
## Summary
66

77
- Total txt files: 69
8-
- Used by Lean library: 24
9-
- Unused: 45
8+
- Used by Lean library: 25
9+
- Unused: 44
1010

1111
## auxiliary
1212

@@ -39,7 +39,7 @@ Generated from a repo scan of `data/ucd/**/*.txt` against Lean library files in
3939
| `data/ucd/core/CaseFolding.txt` | used | UnicodeData/CaseFolding.lean:18 [direct], UnicodeData/CaseFolding.lean:19 [direct], UnicodeData/CaseFolding.lean:22 [direct] (3 matches) |
4040
| `data/ucd/core/CJKRadicals.txt` | unused | |
4141
| `data/ucd/core/CompositionExclusions.txt` | unused | |
42-
| `data/ucd/core/DerivedAge.txt` | unused | |
42+
| `data/ucd/core/DerivedAge.txt` | used | UnicodeData/DerivedAge.lean:11 [direct], UnicodeData/DerivedAge.lean:12 [direct], UnicodeData/DerivedAge.lean:29 [direct], UnicodeData/DerivedAge.lean:31 [direct], ... (6 matches) |
4343
| `data/ucd/core/DerivedCoreProperties.txt` | used | UnicodeData/DerivedCoreProperties.lean:11 [direct], UnicodeData/DerivedCoreProperties.lean:28 [direct], UnicodeData/DerivedCoreProperties.lean:29 [direct], UnicodeData/DerivedCoreProperties.lean:32 [direct], ... (5 matches) |
4444
| `data/ucd/core/DerivedNormalizationProps.txt` | unused | |
4545
| `data/ucd/core/DoNotEmit.txt` | unused | |

lakefile.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ lean_lib UnicodeData where
5454
`UnicodeData.DerivedBinaryProperties,
5555
`UnicodeData.DerivedGeneralCategory,
5656
`UnicodeData.DerivedLineBreak,
57+
`UnicodeData.DerivedAge,
5758
`UnicodeData.BidiBrackets,
5859
`UnicodeData.BidiMirroring,
5960
`UnicodeData.Blocks,

0 commit comments

Comments
 (0)