1use std::fmt;
8use whatlang::{Script, detect_script};
9
10struct LexerRange;
11
12#[derive(PartialEq, Debug)]
13pub struct LexerRegexRange(&'static [(char, char)]);
14
15const RANGE_LATIN: &[(char, char)] = &[('\u{0000}', '\u{024F}')];
16const RANGE_CYRILLIC: &[(char, char)] = &[('\u{0400}', '\u{052F}')];
17const RANGE_ARABIC: &[(char, char)] = &[('\u{0600}', '\u{06FF}'), ('\u{0750}', '\u{077F}')];
18const RANGE_ARMENIAN: &[(char, char)] = &[('\u{0530}', '\u{058F}')];
19const RANGE_DEVANAGARI: &[(char, char)] = &[('\u{0900}', '\u{097F}')];
20const RANGE_HIRAGANA: &[(char, char)] = &[('\u{3040}', '\u{309F}')];
21const RANGE_KATAKANA: &[(char, char)] = &[('\u{30A0}', '\u{30FF}'), ('\u{31F0}', '\u{31FF}')];
22const RANGE_ETHIOPIC: &[(char, char)] = &[('\u{1200}', '\u{139F}'), ('\u{2D80}', '\u{2DDF}')];
23const RANGE_HEBREW: &[(char, char)] = &[('\u{0590}', '\u{05FF}')];
24const RANGE_BENGALI: &[(char, char)] = &[('\u{0980}', '\u{09FF}')];
25const RANGE_GEORGIAN: &[(char, char)] = &[('\u{10A0}', '\u{10FF}'), ('\u{2D00}', '\u{2D2F}')];
26const RANGE_MANDARIN: &[(char, char)] = &[
27 ('\u{4E00}', '\u{9FFF}'),
28 ('\u{3400}', '\u{4DBF}'),
29 ('\u{20000}', '\u{2A6DF}'),
30 ('\u{2A700}', '\u{2CEAF}'),
31];
32const RANGE_HANGUL: &[(char, char)] = &[('\u{1100}', '\u{11FF}'), ('\u{3130}', '\u{318F}')];
33const RANGE_GREEK: &[(char, char)] = &[('\u{0370}', '\u{03FF}'), ('\u{1F00}', '\u{1FFF}')];
34const RANGE_KANNADA: &[(char, char)] = &[('\u{0C80}', '\u{0CFF}')];
35const RANGE_TAMIL: &[(char, char)] = &[('\u{0B80}', '\u{0BFF}')];
36const RANGE_THAI: &[(char, char)] = &[('\u{0E00}', '\u{0E7F}')];
37const RANGE_GUJARATI: &[(char, char)] = &[('\u{0A80}', '\u{0AFF}')];
38const RANGE_GURMUKHI: &[(char, char)] = &[('\u{0A00}', '\u{0A7F}')];
39const RANGE_TELUGU: &[(char, char)] = &[('\u{0C00}', '\u{0C7F}')];
40const RANGE_MALAYALAM: &[(char, char)] = &[('\u{0D00}', '\u{0D7F}')];
41const RANGE_ORIYA: &[(char, char)] = &[('\u{0B00}', '\u{0B7F}')];
42const RANGE_MYANMAR: &[(char, char)] = &[('\u{1000}', '\u{109F}')];
43const RANGE_SINHALA: &[(char, char)] = &[('\u{0D80}', '\u{0DFF}')];
44const RANGE_KHMER: &[(char, char)] = &[('\u{1780}', '\u{17FF}'), ('\u{19E0}', '\u{19FF}')];
45
46impl LexerRange {
47 pub fn from(text: &str) -> Option<&'static [(char, char)]> {
48 detect_script(text).map(|script| match script {
49 Script::Latin => RANGE_LATIN,
50 Script::Cyrillic => RANGE_CYRILLIC,
51 Script::Arabic => RANGE_ARABIC,
52 Script::Armenian => RANGE_ARMENIAN,
53 Script::Devanagari => RANGE_DEVANAGARI,
54 Script::Hiragana => RANGE_HIRAGANA,
55 Script::Katakana => RANGE_KATAKANA,
56 Script::Ethiopic => RANGE_ETHIOPIC,
57 Script::Hebrew => RANGE_HEBREW,
58 Script::Bengali => RANGE_BENGALI,
59 Script::Georgian => RANGE_GEORGIAN,
60 Script::Mandarin => RANGE_MANDARIN,
61 Script::Hangul => RANGE_HANGUL,
62 Script::Greek => RANGE_GREEK,
63 Script::Kannada => RANGE_KANNADA,
64 Script::Tamil => RANGE_TAMIL,
65 Script::Thai => RANGE_THAI,
66 Script::Gujarati => RANGE_GUJARATI,
67 Script::Gurmukhi => RANGE_GURMUKHI,
68 Script::Telugu => RANGE_TELUGU,
69 Script::Malayalam => RANGE_MALAYALAM,
70 Script::Oriya => RANGE_ORIYA,
71 Script::Myanmar => RANGE_MYANMAR,
72 Script::Sinhala => RANGE_SINHALA,
73 Script::Khmer => RANGE_KHMER,
74 })
75 }
76}
77
78impl LexerRegexRange {
79 pub fn from(text: &str) -> Option<Self> {
80 LexerRange::from(text).map(LexerRegexRange)
81 }
82
83 pub fn write_to<W: fmt::Write>(&self, formatter: &mut W) -> Result<(), fmt::Error> {
84 formatter.write_char('[')?;
86
87 for range in self.0 {
88 write!(
89 formatter,
90 "\\x{{{:X}}}-\\x{{{:X}}}",
91 range.0 as u32, range.1 as u32
92 )?;
93 }
94
95 formatter.write_char(']')?;
96
97 Ok(())
98 }
99}
100
101impl Default for LexerRegexRange {
102 fn default() -> Self {
103 LexerRegexRange(RANGE_LATIN)
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn it_gives_ranges() {
113 assert_eq!(LexerRange::from("fox"), Some(RANGE_LATIN));
114 assert_eq!(LexerRange::from("快狐跨懒狗"), Some(RANGE_MANDARIN));
115 assert_eq!(LexerRange::from("Доброе утро."), Some(RANGE_CYRILLIC));
116 }
117
118 #[test]
119 fn it_gives_regex_range() {
120 assert_eq!(
121 LexerRegexRange::from("fox"),
122 Some(LexerRegexRange(RANGE_LATIN))
123 );
124 }
125}
126
127#[cfg(all(feature = "benchmark", test))]
128mod benches {
129 extern crate test;
130
131 use super::*;
132 use test::Bencher;
133
134 #[bench]
135 fn bench_give_ranges_latin(b: &mut Bencher) {
136 b.iter(|| LexerRange::from("fox"));
137 }
138
139 #[bench]
140 fn bench_give_ranges_mandarin(b: &mut Bencher) {
141 b.iter(|| LexerRange::from("快狐跨懒狗"));
142 }
143
144 #[bench]
145 fn bench_give_ranges_cyrillic(b: &mut Bencher) {
146 b.iter(|| LexerRange::from("Доброе утро."));
147 }
148
149 #[bench]
150 fn bench_give_regex_range_latin(b: &mut Bencher) {
151 b.iter(|| LexerRegexRange::from("fox"));
152 }
153}