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   55088 use strict;
  3         5  
  3         66  
3 3     3   10 use warnings;
  3         3  
  3         54  
4 3     3   37 use 5.012000;
  3         6  
5 3     3   9 use utf8;
  3         2  
  3         15  
6 3     3   54 use feature qw/ unicode_strings say /;
  3         3  
  3         203  
7 3     3   11 use charnames qw/ :full lao /;
  3         3  
  3         41  
8 3     3   1560 use version 0.77; our $VERSION = version->declare('v0.0.1');
  3         1246  
  3         15  
9 3     3   192 use Carp;
  3         8  
  3         161  
10 3     3   413 use Class::Accessor::Fast 'antlers';
  3         1984  
  3         22  
11 3     3   549 use Lingua::LO::Transform::Data qw/ is_long_vowel /;
  3         3  
  3         311  
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 29816 my ($class, $syllable) = @_;
107 112         145 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   198 my $s = shift // croak("syllable argument missing");
114              
115 112 50       2724 $s =~ /^$regexp/ or croak "`$s' does not start with a valid syllable";
116 2     2   1464 my %class = ( syllable => $s, parse => { %+ } );
  2         664  
  2         87  
  112         2446  
117 112         803 @class{qw/ consonant end_consonant tone_mark semivowel /} = @+{qw/ consonant end_consonant tone_mark semivowel /};
118              
119 112   100     551 my @vowels = $+{vowel0} // ();
120 112         147 push @vowels, "\N{DOTTED CIRCLE}";
121 112         158 push @vowels, grep { defined } map { $+{"vowel$_"} } 1..3;
  336         353  
  336         967  
122 112         224 $class{vowel} = join('', @vowels);
123              
124 112         134 my $cc = $CONSONANTS{ $class{consonant} }; # consonant category
125 112 100       275 if($+{h}) {
126 5         4 $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       15 unless($H_COMBINERS{ $class{consonant} }) {
131 3         4 $class{end_consonant} = $class{consonant};
132 3         4 $class{consonant} = 'ຫ';
133             }
134 5         4 delete $class{h};
135             }
136 112 100       294 if(is_long_vowel( $class{vowel} )) {
137 65         111 $class{vowel_length} = 'long';
138 65   100     208 $class{tone} = $TONE_MARKS{ $class{tone_mark} // '' }{ $cc };
139             } else {
140 47         85 $class{vowel_length} = 'short';
141 47 100       72 $class{tone} = $cc eq 'TAM' ? 'MID_STOP' : 'HIGH_STOP';
142             }
143             #say Dumper(\%class);
144 112         591 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