@@ -13,17 +13,17 @@ extension Source {
1313 typealias PropertyKind = AST . Atom . CharacterProperty . Kind
1414
1515 static private func withNormalizedForms< T> (
16- _ str: String , match: ( String ) -> T ?
17- ) -> T ? {
16+ _ str: String , match: ( String ) throws -> T ?
17+ ) rethrows -> T ? {
1818 // This follows the rules provided by UAX44-LM3, including trying to drop an
1919 // "is" prefix, which isn't required by UTS#18 RL1.2, but is nice for
2020 // consistency with other engines and the Unicode.Scalar.Properties names.
2121 let str = str. filter { !$0. isPatternWhitespace && $0 != " _ " && $0 != " - " }
2222 . lowercased ( )
23- if let m = match ( str) {
23+ if let m = try match ( str) {
2424 return m
2525 }
26- if str. hasPrefix ( " is " ) , let m = match ( String ( str. dropFirst ( 2 ) ) ) {
26+ if str. hasPrefix ( " is " ) , let m = try match ( String ( str. dropFirst ( 2 ) ) ) {
2727 return m
2828 }
2929 return nil
@@ -79,6 +79,19 @@ extension Source {
7979 }
8080 }
8181
82+ static private func classifyNumericType(
83+ _ str: String
84+ ) -> Unicode . NumericType ? {
85+ withNormalizedForms ( str) { str in
86+ switch str {
87+ case " decimal " : return . decimal
88+ case " digit " : return . digit
89+ case " numeric " : return . numeric
90+ default : return nil
91+ }
92+ }
93+ }
94+
8295 static private func classifyBoolProperty(
8396 _ str: String
8497 ) -> Unicode . BinaryProperty ? {
@@ -361,6 +374,27 @@ extension Source {
361374 }
362375 }
363376 }
377+
378+ static func parseAge( _ value: String ) -> Unicode . Version ? {
379+ // Age can be specified in the form '3.0' or 'V3_0'.
380+ // Other formats are not supported.
381+ var str = value [ ... ]
382+
383+ let separator : Character
384+ if str. first == " V " {
385+ str. removeFirst ( )
386+ separator = " _ "
387+ } else {
388+ separator = " . "
389+ }
390+
391+ guard let sepIndex = str. firstIndex ( of: separator) ,
392+ let major = Int ( str [ ..< sepIndex] ) ,
393+ let minor = Int ( str [ sepIndex... ] . dropFirst ( ) )
394+ else { return nil }
395+
396+ return ( major, minor)
397+ }
364398
365399 static func classifyCharacterPropertyValueOnly(
366400 _ value: String
@@ -414,22 +448,51 @@ extension Source {
414448
415449 // This uses the aliases defined in
416450 // https://www.unicode.org/Public/UCD/latest/ucd/PropertyAliases.txt.
417- let match = withNormalizedForms ( key) { key -> PropertyKind ? in
418- switch key {
451+ let match = try withNormalizedForms ( key) { normalizedKey -> PropertyKind ? in
452+ switch normalizedKey {
419453 case " script " , " sc " :
420- if let script = classifyScriptProperty ( value) {
421- return . script ( script )
454+ guard let script = classifyScriptProperty ( value) else {
455+ throw ParseError . unrecognizedScript ( value )
422456 }
457+ return . script( script)
423458 case " scriptextensions " , " scx " :
424- if let script = classifyScriptProperty ( value) {
425- return . scriptExtension ( script )
459+ guard let script = classifyScriptProperty ( value) else {
460+ throw ParseError . unrecognizedScript ( value )
426461 }
462+ return . scriptExtension( script)
427463 case " gc " , " generalcategory " :
428- if let cat = classifyGeneralCategory ( value) {
429- return . generalCategory( cat)
464+ guard let cat = classifyGeneralCategory ( value) else {
465+ throw ParseError . unrecognizedCategory ( value)
466+ }
467+ return . generalCategory( cat)
468+ case " age " :
469+ guard let ( major, minor) = parseAge ( value) else {
470+ throw ParseError . invalidAge ( value)
430471 }
472+ return . age( major: major, minor: minor)
431473 case " name " , " na " :
432474 return . named( value)
475+ case " numericvalue " , " nv " :
476+ guard let numericValue = Double ( value) else {
477+ throw ParseError . invalidNumericValue ( value)
478+ }
479+ return . numericValue( numericValue)
480+ case " numerictype " , " nt " :
481+ guard let type = classifyNumericType ( value) else {
482+ throw ParseError . unrecognizedNumericType ( value)
483+ }
484+ return . numericType( type)
485+ case " slc " , " simplelowercasemapping " :
486+ return . mapping( . lowercase, value)
487+ case " suc " , " simpleuppercasemapping " :
488+ return . mapping( . uppercase, value)
489+ case " stc " , " simpletitlecasemapping " :
490+ return . mapping( . titlecase, value)
491+ case " ccc " , " canonicalcombiningclass " :
492+ guard let cccValue = UInt8 ( value) , cccValue <= 254 else {
493+ throw ParseError . invalidCCC ( value)
494+ }
495+ return . ccc( . init( rawValue: cccValue) )
433496 default :
434497 break
435498 }
0 commit comments