Module: TWFilter::Punctuation
- Defined in:
- lib/twfilter/punctuation.rb,
sig/twfilter.rbs
Constant Summary collapse
- TAIWAN =
",。、;:?!「」『』()〔〕【】《》〈〉──……‧—".chars.to_set.freeze
- LATIN =
".,;:?!()[]{}\"'-/%&+=*\#@_<>|$^`\\".chars.to_set.freeze
- SPACE =
" \t\u{3000}\u{00A0}".chars.to_set.freeze
- STRICT =
STRICT is the 教育部 handbook inventory; WIDE adds marks in common Taiwanese use but outside it.
(TAIWAN | LATIN | SPACE | "…⋯.·─".chars.to_set).freeze
- WIDE =
(STRICT | "~~℃°※○●–→§±×÷№‼⁉".chars.to_set).freeze
- SETS =
{strict: STRICT, wide: WIDE}.freeze
- ALLOWED =
WIDE- BOPOMOFO =
Bopomofo and its extended block: the Taiwanese phonetic script, admitted as text.
[0x3100..0x312F, 0x31A0..0x31BF].freeze
- TONE_MARKS =
"\u{02CA}\u{02C7}\u{02CB}\u{02D9}\u{02C9}".chars.to_set.freeze
- INVISIBLE =
/[\u{200B}-\u{200F}\u{2028}\u{2029}\u{FEFF}\u{FE00}-\u{FE0F}\u{00AD}]/- WIDTH =
{ """ => "\"", "#" => "#", "$" => "$", "%" => "%", "&" => "&", "'" => "'", "*" => "*", "+" => "+", "-" => "-", "." => ".", "/" => "/", "<" => "<", "=" => "=", ">" => ">", "@" => "@", "[" => "[", "\" => "\\", "]" => "]", "^" => "^", "_" => "_", "`" => "`", "{" => "{", "|" => "|", "}" => "}" }.freeze
- RULES =
[ [INVISIBLE, ""], [/[#{Regexp.escape(WIDTH.keys.join)}]/, WIDTH], # Fullwidth digits and Latin letters fold to ASCII; 0xFEE0 is the fixed offset between the blocks. [/[\u{FF10}-\u{FF19}\u{FF21}-\u{FF3A}\u{FF41}-\u{FF5A}]/, -> (m) { (m.ord - 0xFEE0).chr(Encoding::UTF_8) }], [/[\u{201C}\u{301D}]/, "「"], [/[\u{201D}\u{301E}\u{301F}]/, "」"], [/\u{2018}(?=[^\u{2019}]*\p{Han})/, "『"], [/(?<=\p{Han})\u{2019}/, "』"], [/\u{FE50}/, ","], [/\u{FE55}|\u{FE30}|\u{FE13}/, ":"], [/[\u{30FB}\u{FF65}]/, "\u{2027}"], [/(?<=\p{Han})[\u{00B7}\u{2022}\u{2219}]|[\u{00B7}\u{2022}\u{2219}](?=\p{Han})/, "\u{2027}"], [/(?:\u{2026}|\u{22EF}|。。|\.{3,}|・{3,}){1,}/, "\u{2026}\u{2026}"], [/[\u{2014}\u{2015}\u{2500}]{1,}|(?<=\p{Han})--(?=\p{Han})/, "\u{2500}\u{2500}"], ["\u{301C}", "~"], [/(?<=\p{Han}),(?=\s|\p{Han}|\z)/, ","], [/(?<=\p{Han})\.(?=\s|\p{Han}|\z)/, "。"], [/(?<=\p{Han})\?(?=\s|\p{Han}|\z)/, "?"], [/(?<=\p{Han})!(?=\s|\p{Han}|\z)/, "!"], [/(?<=\p{Han});(?=\s|\p{Han}|\z)/, ";"], [/(?<=\p{Han}):(?=\s|\p{Han}|\z)/, ":"], [/(?<=\p{Han})\s+(?=\p{Han})/, ""], [/\s+(?=[,。、;:?!」』)〕】》〉])/, ""], [/(?<=[,。、;:?!「『(〔【《〈])\s+/, ""], [/[ \t\u{00A0}]{2,}/, " "] ].freeze
- COLLAPSE =
/([,。!?;:])\1+/
Class Method Summary collapse
- .acceptable?(char, set: WIDE) ⇒ Boolean
- .normalize(text, collapse_repeats: true) ⇒ ::String
- .offenders(text, punctuation: :wide) ⇒ ::Array[::String]
Class Method Details
.acceptable?(char, set: WIDE) ⇒ Boolean
93 94 95 96 97 98 99 |
# File 'lib/twfilter/punctuation.rb', line 93 def acceptable?(char, set: WIDE) return true if Han.char?(char) || set.include?(char) || TONE_MARKS.include?(char) return true if char.match?(/[A-Za-z0-9]/) code = char.ord BOPOMOFO.any? { |range| range.cover?(code) } end |
.normalize(text, collapse_repeats: true) ⇒ ::String
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/twfilter/punctuation.rb', line 78 def normalize(text, collapse_repeats: true) value = RULES.reduce(text.to_s.unicode_normalize(:nfc)) { |memo, (pattern, replacement)| case replacement in Proc memo.gsub(pattern) { replacement.call(Regexp.last_match(0)) } in Hash memo.gsub(pattern, replacement) in String memo.gsub(pattern, replacement) end } value = value.gsub(COLLAPSE, "\\1") if collapse_repeats value.strip end |
.offenders(text, punctuation: :wide) ⇒ ::Array[::String]
101 102 103 104 |
# File 'lib/twfilter/punctuation.rb', line 101 def offenders(text, punctuation: :wide) set = SETS.fetch(punctuation) text.each_char.reject { |char| acceptable?(char, set: set) }.uniq end |