File Coverage

blib/lib/Lingua/Phonology/Common.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Lingua::Phonology::Common;
4              
5             # This module is used for functions needed at least in part by all other
6             # packages.
7              
8             # We export everything. Since this is only for internal use, we know what we're
9             # getting, and the funcs all begin with _, so are unlikely to clash anyway
10             @ISA = qw(Exporter);
11             @EXPORT = qw(
12             _err
13             _is
14             _is_features
15             _is_symbols
16             _is_syllable
17             _is_seg
18             _is_boundary
19             _is_ruleseg
20             _is_tier
21             _to_handle
22             _parse_from_file
23             _parse_from_string
24             _string_from_struct
25             _parse_ext
26             _parse_plain
27             _deparse_ext
28             );
29              
30             $VERSION = 0.1;
31              
32 10     10   58 use strict;
  10         19  
  10         429  
33 10     10   54 use warnings::register;
  10         19  
  10         2120  
34              
35 10     10   185 use Carp qw/carp croak/;
  10         24  
  10         1024  
36             our @CARP_NOT = qw/
37             Lingua::Phonology
38             Lingua::Phonology::Features
39             Lingua::Phonology::Symbols
40             Lingua::Phonology::Segment
41             Lingua::Phonology::Segment::Rules
42             Lingua::Phonology::Segment::Tier
43             Lingua::Phonology::Segment::Boundary
44             Lingua::Phonology::Rules
45             Lingua::Phonology::Syllable
46             Lingua::Phonology::Word
47             /;
48 10     10   32379 use IO::Handle;
  10         112913  
  10         2527  
49 10     10   26652 use XML::Simple;
  0            
  0            
50              
51             # Global variables. In principle, modules using this module can change these if
52             # they want, but they probably shouldn't lest evil things transpire.
53             our %xmlin_opts = (
54             KeyAttr => { feature => 'name', child => 'name', parent => 'name', symbol => 'label' },
55             ForceArray => [qw/child parent feature symbol rule/],
56             GroupTags => { features => 'feature', symbols => 'symbol', order => 'block', persist => 'rule', block => 'rule' }
57             );
58             our %xmlout_opts = (
59             KeepRoot => 1,
60             KeyAttr => { feature => 'name', child => 'name', parent => 'name', symbol => 'label', rule => 'name' }
61             );
62              
63             # Concise synonym for UNIVERSAL::isa() with automatic error-writing
64             sub _is($$) {
65             UNIVERSAL::isa(@_);
66             }
67              
68             # Extensions of _is for our own classes
69             sub _is_features ($) { _is(shift, 'Lingua::Phonology::Features') }
70             sub _is_symbols ($) { _is(shift, 'Lingua::Phonology::Symbols') }
71             sub _is_syllable ($) { _is(shift, 'Lingua::Phonology::Syllable') }
72             sub _is_boundary ($) { _is(shift, 'Lingua::Phonology::Segment::Boundary') }
73             sub _is_ruleseg ($) { _is(shift, 'Lingua::Phonology::Segment::Rules') }
74             sub _is_tier ($) { _is(shift, 'Lingua::Phonology::Segment::Tier') }
75              
76             # _is_seg is hacked to allow various segment lookalikes
77             sub _is_seg ($) {
78             my $seg = shift;
79             return _is($seg, 'Lingua::Phonology::Segment')
80             || _is($seg, 'Lingua::Phonology::Segment::Rules')
81             || _is($seg, 'Lingua::Phonology::Segment::Tier');
82             }
83              
84             # Make a handle from a filename; don't touch existing handles
85             sub _to_handle($$) {
86             my ($file, $mode) = @_;
87             return $file if _is($file, 'GLOB');
88              
89             my $handle = IO::Handle->new();
90             open $handle, $mode, $file or croak "Couldn't open $file: $!";
91             return $handle;
92             }
93              
94             # Get the parsed XML structure from a filename. Optional second arg specifies
95             # which key of the parse to return. You'd better specify a key that's present
96             # on the topmost level of the parse--this method won't look through the whole
97             # structure for you, like the previous version did.
98              
99             sub _parse_from_file ($;$) {
100             my $file = shift;
101              
102             # Open, slurp, close
103             $file = _to_handle($file, '<') or return;
104             my $string = join '', <$file>;
105             close $file;
106              
107             return _parse_from_string($string, @_);
108             }
109              
110             sub _parse_from_string ($;$) {
111             my ($string, $element) = @_;
112              
113             # Parse the string, check for errors
114             my $parse;
115             eval { $parse = XMLin($string, %xmlin_opts) };
116             croak "XML parsing error: $@" if ($@);
117              
118             if (defined $element) {
119             return $parse->{$element} if exists $parse->{$element};
120             croak "<$element> element not found";
121             }
122             return $parse;
123             }
124              
125             # Turn a data structure into an XML string
126             sub _string_from_struct ($) {
127             my $struct = shift;
128              
129             my $string;
130             eval { $string = XMLout($struct, %xmlout_opts) };
131             croak "Error creating XML: $@" if $@;
132              
133             return $string;
134             }
135              
136             sub _parse_ext ($) {
137             my $string = shift;
138             $string =~ s/(-?\d+):/\$_[$1]->/g;
139             return eval "return sub { package main; $string }";
140             }
141              
142             sub _parse_plain ($) {
143             return eval "return sub { package main; $_[0] }";
144             }
145              
146             sub _deparse_ext ($$) {
147             my ($code, $deparser) = @_;
148             my $string = $deparser->coderef2text($code);
149             $string =~ s/\{(.*)\}/$1/s; # Strip opening/closing brackets
150             #$string =~ s/^\s*(.*?)\s*$/$1/s; # String leading/trailing whitespace
151             $string =~ s/\$_\[(-?\d+)\]->/$1:/gs; # Do ext conversion
152             return $string;
153             }
154              
155             sub _err ($) {
156             carp shift;
157             return;
158             }
159              
160             1;
161