Skip to content

Commit c8bc12a

Browse files
authored
feat: add Script property (#126)
1 parent 869d787 commit c8bc12a

13 files changed

Lines changed: 4611 additions & 0 deletions

File tree

UnicodeBasic.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,27 @@ namespace Unicode
5858
@[inline]
5959
def getName (char : Char) : String := lookupName char.val
6060

61+
/-!
62+
## Script ##
63+
-/
64+
65+
/-- Get character script
66+
67+
Unicode property: `Script`
68+
-/
69+
@[inline]
70+
def getScript (char : Char) : Script := lookupScript char.val
71+
72+
/-- Get script name
73+
74+
Returns `none` if the script code is unassigned.
75+
76+
Unicode property: `Script`
77+
-/
78+
@[inline]
79+
def getScriptName? (s : Script) : Option String :=
80+
lookupScriptName s |>.map toString
81+
6182
/-!
6283
## Bidirectional Properties ##
6384
-/

UnicodeBasic/TableLookup.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ protected abbrev oMath : UInt64 := 0x800000000
2222
@[extern "unicode_prop_lookup"]
2323
protected opaque lookupProp (c : UInt32) : UInt64
2424

25+
@[extern "unicode_script_lookup"]
26+
protected opaque lookupScript (c : UInt32) : Script
27+
2528
end CLib
2629

2730
/-- Binary search -/
@@ -386,4 +389,22 @@ where
386389
str : String := include_str "../data/table/White_Space.txt"
387390
table : Thunk <| Array (UInt32 × UInt32) := parsePropTable str
388391

392+
/-- Get the script of a code point using lookup table
393+
394+
Unicode property: `Script` -/
395+
@[inline]
396+
def lookupScript (c : UInt32) : Script := CLib.lookupScript c
397+
398+
/-- Get the name of a script
399+
400+
Unicode property: `Script` -/
401+
def lookupScriptName (s : Script) : Option String.Slice :=
402+
let table := table.get
403+
if s.code < table[0]!.1 then none else
404+
match table[find s.code (fun i => table[i]!.1) 0 table.size.toUSize]! with
405+
| (c, v) => if s.code = c then some v else none
406+
where
407+
str : String := include_str "../data/table/Script_Name.txt"
408+
table : Thunk <| Array (UInt32 × String.Slice) := parseTable str fun _ n => n[0]!
409+
389410
end Unicode

UnicodeBasic/Types.lean

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Copyright © 2023-2025 François G. Dorais. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
-/
55

6+
import Std.Data.HashMap
7+
68
/-- Low-level conversion from `UInt32` to `Char` (*unsafe*)
79
810
This function translates to a no-op in the compiler. However, it does not
@@ -1011,4 +1013,64 @@ def BidiClass.ofAbbrev! (abbr : String.Slice) : BidiClass :=
10111013
instance : Repr BidiClass where
10121014
reprPrec bc _ := s!"Unicode.BidiClass.{bc.toAbbrev}"
10131015

1016+
/-!
1017+
## Scripts ##
1018+
-/
1019+
1020+
/-- Check if valid script identifier -/
1021+
@[inline]
1022+
def Script.isValid (c : UInt32) : Bool :=
1023+
let c0 := (c >>> 24).toUInt8
1024+
let c1 := (c >>> 16).toUInt8
1025+
let c2 := (c >>> 8).toUInt8
1026+
let c3 := c.toUInt8
1027+
(c0 ≤ 'Z'.toUInt8 && 'A'.toUInt8 ≤ c0)
1028+
&& (c1 ≤ 'z'.toUInt8 && 'a'.toUInt8 ≤ c1)
1029+
&& (c2 ≤ 'z'.toUInt8 && 'a'.toUInt8 ≤ c2)
1030+
&& (c3 ≤ 'z'.toUInt8 && 'a'.toUInt8 ≤ c3)
1031+
1032+
/-- Script identifier type -/
1033+
structure Script where
1034+
code : UInt32
1035+
is_valid : Script.isValid code
1036+
deriving DecidableEq, Hashable
1037+
1038+
namespace Script
1039+
1040+
/-- Default value is `Zzzz` (`Unknown`) -/
1041+
instance : Inhabited Script where
1042+
default := {
1043+
code := (((('Z'.val <<< 8 ||| 'z'.val) <<< 8) ||| 'z'.val) <<< 8) ||| 'z'.val
1044+
is_valid := by decide
1045+
}
1046+
1047+
/-- String abbreviation of script -/
1048+
@[extern "unicode_script_to_abbrev"]
1049+
def toAbbrev : Script → String
1050+
| ⟨c, _⟩ =>
1051+
let c0 := Char.ofUInt8 (c >>> 24).toUInt8
1052+
let c1 := Char.ofUInt8 (c >>> 16).toUInt8
1053+
let c2 := Char.ofUInt8 (c >>> 8).toUInt8
1054+
let c3 := Char.ofUInt8 c.toUInt8
1055+
String.ofList [c0, c1, c2, c3]
1056+
1057+
@[extern "unicode_script_of_abbrev"]
1058+
private opaque ofAbbrevAux (abbr : String) : UInt32
1059+
1060+
/-- Get script from abbreviation -/
1061+
def ofAbbrev? (abbr : String.Slice) : Option Script :=
1062+
if abbr.utf8ByteSize = 4 then
1063+
let code := ofAbbrevAux abbr.toString
1064+
if h : Script.isValid code then
1065+
some ⟨code, h⟩
1066+
else
1067+
none
1068+
else
1069+
none
1070+
1071+
@[inline, inherit_doc ofAbbrev?]
1072+
def ofAbbrev! (abbr : String.Slice) : Script := ofAbbrev? abbr |>.get!
1073+
1074+
end Script
1075+
10141076
end Unicode

UnicodeCLib/basic.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <lean/lean.h>
2+
#include <arpa/inet.h>
3+
#include "basic.h"
4+
5+
static inline int unicode_script_is_valid(uint32_t c) {
6+
int c0 = (int)(c >> 24 & 0xff);
7+
int c1 = (int)(c >> 16 & 0xff);
8+
int c2 = (int)(c >> 8 & 0xff);
9+
int c3 = (int)(c & 0xff);
10+
return
11+
c0 <= 'Z' && 'A' <= c0 &&
12+
c1 <= 'z' && 'a' <= c1 &&
13+
c2 <= 'z' && 'a' <= c2 &&
14+
c3 <= 'z' && 'a' <= c3;
15+
}
16+
17+
uint32_t unicode_script_of_abbrev(b_lean_obj_arg abbr) {
18+
lean_string_object * str = lean_to_string(abbr);
19+
assert(str->m_size > 4);
20+
uint32_t val = *((uint32_t*)(str->m_data));
21+
return ntohl(val);
22+
}
23+
24+
lean_obj_res unicode_script_to_abbrev(uint32_t scr) {
25+
assert(unicode_script_is_valid(scr));
26+
lean_object * abbr = lean_alloc_string(5, 5, 4);
27+
lean_to_string(abbr)->m_data[4] = 0;
28+
*((uint32_t*)lean_to_string(abbr)->m_data) = htonl(scr);
29+
return abbr;
30+
}

UnicodeCLib/basic.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ LEAN_EXPORT uint64_t unicode_prop_lookup(uint32_t c);
6969

7070
LEAN_EXPORT uint64_t unicode_case_lookup(uint32_t c);
7171

72+
LEAN_EXPORT uint32_t unicode_script_lookup(uint32_t c);
73+
74+
LEAN_EXPORT uint32_t unicode_script_of_abbrev(b_lean_obj_arg abbr);
75+
76+
LEAN_EXPORT lean_obj_res unicode_script_to_abbrev(uint32_t scr);
77+
7278
#ifdef __cplusplus
7379
}
7480
#endif

0 commit comments

Comments
 (0)