File Coverage

blib/lib/Lingua/LO/Transform/Analyze.pm
Criterion Covered Total %
statement 57 57 100.0
branch 9 10 90.0
condition 6 8 75.0
subroutine 13 13 100.0
pod 1 1 100.0
total 86 89 96.6


line stmt bran cond sub pod time code
1             package Lingua::LO::Transform::Analyze;
2 3     3   26417 use strict;
  3         3  
  3         72  
3 3     3   9 use warnings;
  3         3  
  3         80  
4 3     3   50 use 5.012000;
  3         9  
5 3     3   10 use utf8;
  3         4  
  3         14  
6 3     3   61 use feature qw/ unicode_strings say /;
  3         6  
  3         918  
7 3     3   499 use charnames qw/ :full lao /;
  3         22386  
  3         52  
8 3     3   10110 use version 0.77; our $VERSION = version->declare('v0.0.1');
  3         1382  
  3         16  
9 3     3   210 use Carp;
  3         3  
  3         208  
10 3     3   409 use Class::Accessor::Fast 'antlers';
  3         1938  
  3         23  
11 3     3   596 use Lingua::LO::Transform::Data qw/ is_long_vowel /;
  3         4  
  3         331  
12              
13             =encoding UTF-8
14              
15             =head1 NAME
16              
17             Lingua::LO::Transform::Analyze - Analyze a Lao syllable and provide accessors to its constituents
18              
19             =head1 FUNCTION
20              
21             Objects of this class represent a Lao syllable with an analysis of its
22             constituents. After passing a valid syllable to the constructor, the parts are
23             available via accessor methods as outlined below.
24              
25             =cut
26              
27             for my $attribute (qw/ syllable parse vowel consonant end_consonant vowel_length tone tone_mark h semivowel /) {
28             has $attribute => (is => 'ro');
29             }
30              
31             my %TONE_MARKS = (
32             "" => {
33             SUNG => 'LOW_RISING',
34             KANG => 'LOW',
35             TAM => 'HIGH',
36             },
37             "\N{LAO TONE MAI EK}" => {
38             SUNG => 'MID',
39             KANG => 'MID',
40             TAM => 'MID',
41             },
42             "\N{LAO TONE MAI THO}" => {
43             SUNG => 'MID_FALLING',
44             KANG => 'HIGH_FALLING',
45             TAM => 'HIGH_FALLING',
46             },
47             "\N{LAO TONE MAI TI}" => { }, # TODO
48             "\N{LAO TONE MAI CATAWA}" => { } # TODO
49             );
50              
51             my %CONSONANTS = (
52             ກ => 'KANG',
53             ຂ => 'SUNG',
54             ຄ => 'TAM',
55             ງ => 'TAM',
56             ຈ => 'KANG',
57             ສ => 'SUNG',
58             ຊ => 'TAM',
59             ຍ => 'TAM',
60             ດ => 'KANG',
61             ຕ => 'KANG',
62             ຖ => 'SUNG',
63             ທ => 'TAM',
64             ນ => 'TAM',
65             ບ => 'KANG',
66             ປ => 'KANG',
67             ຜ => 'SUNG',
68             ຝ => 'SUNG',
69             ພ => 'TAM',
70             ຟ => 'TAM',
71             ມ => 'TAM',
72             ຢ => 'KANG',
73             ລ => 'TAM',
74             ວ => 'TAM',
75             ຫ => 'SUNG',
76             ອ => 'KANG',
77             ຮ => 'TAM',
78             ຣ => 'TAM',
79             ຫງ => 'SUNG',
80             ຫຍ => 'SUNG',
81             ຫນ => 'SUNG',
82             ໜ => 'SUNG',
83             ຫມ => 'SUNG',
84             ໝ => 'SUNG',
85             ຫລ => 'SUNG',
86             ຫຼ => 'SUNG',
87             ຫວ => 'SUNG',
88             );
89              
90             my %H_COMBINERS = map { $_ => 1 } qw/ ຍ ວ /;
91              
92             =head1 METHODS
93              
94             =head2 new
95              
96             C
97              
98             The constructor takes a syllable as its only argument. It does not fail but may
99             produce nonsense if the argument is not valid according to Lao morphology
100             rules. See L if your input doesn't
101             come from this class already.
102              
103             =cut
104              
105             sub new {
106 112     112 1 30076 my ($class, $syllable) = @_;
107 112         153 return bless _classify($syllable), $class;
108             }
109              
110             my $regexp = Lingua::LO::Transform::Data::get_sylre_named;
111              
112             sub _classify {
113 112   33 112   186 my $s = shift // croak("syllable argument missing");
114              
115 112 50       2752 $s =~ /^$regexp/ or croak "`$s' does not start with a valid syllable";
116 2     2   1527 my %class = ( syllable => $s, parse => { %+ } );
  2         704  
  2         89  
  112         2503  
117 112         834 @class{qw/ consonant end_consonant tone_mark semivowel /} = @+{qw/ consonant end_consonant tone_mark semivowel /};
118              
119 112   100     570 my @vowels = $+{vowel0} // ();
120 112         152 push @vowels, "\N{DOTTED CIRCLE}";
121 112         130 push @vowels, grep { defined } map { $+{"vowel$_"} } 1..3;
  336         355  
  336         976  
122 112         210 $class{vowel} = join('', @vowels);
123              
124 112         141 my $cc = $CONSONANTS{ $class{consonant} }; # consonant category
125 112 100       313 if($+{h}) {
126 5         7 $cc = 'SUNG'; # $CONSONANTS{'ຫ'}
127              
128             # If there is a preceding vowel, it uses the ຫ as a consonant and the
129             # one parsed as core consonant is actually an end consonant
130 5 100       13 unless($H_COMBINERS{ $class{consonant} }) {
131 3         5 $class{end_consonant} = $class{consonant};
132 3         5 $class{consonant} = 'ຫ';
133             }
134 5         6 delete $class{h};
135             }
136 112 100       280 if(is_long_vowel( $class{vowel} )) {
137 65         129 $class{vowel_length} = 'long';
138 65   100     217 $class{tone} = $TONE_MARKS{ $class{tone_mark} // '' }{ $cc };
139             } else {
140 47         86 $class{vowel_length} = 'short';
141 47 100       87 $class{tone} = $cc eq 'TAM' ? 'MID_STOP' : 'HIGH_STOP';
142             }
143             #say Dumper(\%class);
144 112         637 return \%class;
145             }
146              
147             =head2 ACCESSORS
148              
149              
150             =head3 syllable
151              
152             The original syllable as passed to the constructor
153              
154             =head3 parse
155              
156             A hash of raw constituents as returned by the parsing regexp. Although the
157             other accessors present constituents in a more accessible way and take care of
158             morphological special cases like the treatment of ຫ, this may come in handy to
159             quickly check e.g. if there was a vowel component before the core consonant.
160              
161             =head3 vowel
162              
163             The syllable's vowel or diphthong. As the majority of vowels have more than one
164             code point, the consonant position is represented by the unicode sign
165             designated for this function, DOTTED CIRCLE or U+25CC.
166              
167             =head3 consonant
168              
169             The syllable's core consonant
170              
171             =head3 end_consonant
172              
173             The end consonant if present, C otherwise.
174              
175             =head3 tone_mark
176              
177             The tone mark if present, C otherwise.
178              
179             =head3 semivowel
180              
181             The semivowel following the core consonant if present, C otherwise.
182              
183             =head3 h
184              
185             "ຫ" if the syllable contained a combining ຫ, i.e. one that isn't the core consonant.
186              
187             =head3 vowel_length
188              
189             The string 'long' or 'short'.
190              
191             =head3 tone
192              
193             One of the following strings, depending on core consonant class, vowel length and tone mark:
194              
195             =over 4
196              
197             =item LOW_RISING
198              
199             =item LOW
200              
201             =item HIGH
202              
203             =item MID_FALLING
204              
205             =item HIGH_FALLING
206              
207             =item MID_STOP
208              
209             =item HIGH_STOP
210              
211             =back
212              
213             The latter two occur with short vowels, the other ones with long vowels.
214              
215             =cut
216              
217             1;
218