File Coverage

blib/lib/Lingua/Phonology/Features.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 -w
2              
3             package Lingua::Phonology::Features;
4              
5             =head1 NAME
6              
7             Lingua::Phonology::Features - a module to handle a set of hierarchical
8             features.
9              
10             =head1 SYNOPSIS
11              
12             use Lingua::Phonology;
13              
14             my $phono = new Lingua::Phonology;
15             my $features = $phono->features;
16              
17             # Add features programmatically
18             $features->add_feature(
19             Node => { type => 'privative', children => ['Scalar', 'Binary', 'Privative'] },
20             Scalar => { type => 'scalar' },
21             Binary => { type => 'binary' },
22             Privative => { type => 'privative' }
23             );
24              
25             # Drop features
26             $features->drop_feature('Privative');
27              
28             # Load feature definitions from a file
29             $features->loadfile('phono.xml');
30              
31             # Load default features
32             $features->loadfile;
33              
34              
35             =head1 DESCRIPTION
36              
37             Lingua::Phonology::Features allows you to create a hierarchy of features of
38             various types, and includes methods for adding and deleting features and
39             changing the relationships between them.
40              
41             By "heirarchical features" we mean that some features dominate some other
42             features, as in a tree. By having heirarchical features, it becomes possible to
43             set multiple features at once by assigning to a node, and to indicate
44             conceptually related features that are combined under the same node. This
45             module, however, does not instantiate values of features, but only establishes
46             the relationships between features.
47              
48             Lingua::Phonology::Features recognizes multiple types of features. Features
49             may be privative (which means that their legal values are either true or
50             C), binary (which means they may be true, false, or C), or scalar
51             (which means that their legal value may be anything). You can freely mix
52             different kinds of features into the same set of features.
53              
54             Finally, while this module provides a full set of methods to add and delete
55             features programmatically, it also provides the option of reading feature
56             definitions from a file. This is usually faster and more convenient. The
57             method to do this is L<"loadfile">. Lingua::Phonology::Features also comes
58             with an extensive default feature set.
59              
60             =cut
61              
62 2     2   44287 use strict;
  2         8  
  2         90  
63 2     2   11 use warnings;
  2         15  
  2         553  
64 2     2   21 use warnings::register;
  2         4  
  2         319  
65 2     2   12 use Carp;
  2         3  
  2         172  
66 2     2   3602 use Lingua::Phonology::Common;
  0            
  0            
67              
68             sub err ($) { _err($_[0]) if warnings::enabled() };
69              
70             our $VERSION = 0.2;
71              
72             # %valid defines valid feature types
73             my %valid = (
74             privative => 1,
75             binary => 1,
76             scalar => 1,
77             node => 1
78             );
79              
80             # Constructor
81             sub new {
82             my $proto = shift;
83             my $class = ref($proto) || $proto;
84             bless {}, $class;
85             }
86              
87             # Add features to our set
88             sub add_feature {
89             my $self = shift;
90             my %hash = @_;
91             my $err = 0;
92              
93             FEATURE: for (keys(%hash)) {
94             unless (_is($hash{$_}, 'HASH')) {
95             err("Bad value for $_");
96             $err = 1;
97             next FEATURE;
98             }
99              
100             # Error checking--these invalidate the whole feature
101             if (not $hash{$_} = _check_featureref($_, $hash{$_})) {
102             $err = 1;
103             next FEATURE;
104             }
105              
106             # Drop any old feature
107             $self->drop_feature($_) if $self->feature_exists($_);
108              
109             # Add the new feature
110             $self->_add_featureref($_, $hash{$_}) or $err = 1;
111             }
112              
113             return $err ? () : 1;
114             }
115              
116             # Change an existing feature. Same as add_feature(), but checks that the
117             # feature exists first
118             sub change_feature {
119             my ($self, %hash) = @_;
120             my $err = 0;
121              
122             FEATURE: for (keys(%hash)) {
123             if (not $self->feature($_)) {
124             $err = 1;
125             next FEATURE;
126             }
127              
128             if (not _is($hash{$_}, 'HASH')) {
129             err "Bad value for $_";
130             $err = 1;
131             next FEATURE;
132             }
133              
134             # Check the href
135             if (not $hash{$_} = _check_featureref($_, $hash{$_})) {
136             $err = 1;
137             next FEATURE;
138             }
139              
140             # Add the ref
141             $self->_add_featureref($_, $hash{$_}) or $err = 1;
142              
143             }
144             return $err ? () : 1;
145             }
146              
147             # Private -- check a hashref
148             sub _check_featureref {
149             my ($name, $ref) = @_;
150              
151             # Check types
152             $ref->{type} = lc $ref->{type};
153             if (not $valid{$ref->{type}}) {
154             return err("Invalid feature type '$ref->{type}' for feature $name");
155             }
156             $ref->{type} = 'privative' if $ref->{type} eq 'node';
157              
158             # Check children
159             if ($ref->{child} && not _is($ref->{child}, 'ARRAY')) {
160             return err("Bad value for child of $name");
161             }
162             $ref->{child} ||= [];
163              
164             # Check parents
165             if ($ref->{parent} && not _is($ref->{parent}, 'ARRAY')) {
166             return err("Bad value for parent of $name");
167             }
168             $ref->{parent} ||= [];
169              
170             # All OK
171             return $ref;
172             }
173              
174             # Private -- apply a hashref
175             sub _add_featureref {
176             my ($self, $name, $ref) = @_;
177             my $err = 0;
178              
179             $self->{$name}->{type} = $ref->{type};
180             $self->{$name}->{child} = {};
181             $self->{$name}->{parent} = {};
182             $self->add_child($name, @{$ref->{child}}) or $err = 1;
183             $self->add_parent($name, @{$ref->{parent}}) or $err = 1;
184             return $err ? () : 1;
185             }
186              
187              
188             # Get a feature or get warned
189             sub feature {
190             my ($self, $feature) = @_;
191             return $self->{$feature} if exists($self->{$feature});
192             return err("No such feature '$feature'");
193             }
194              
195             # Check if a feature exists (w/o warnings)
196             sub feature_exists {
197             my $self = shift;
198             exists $self->{$_[0]};
199             }
200              
201             # Drop a feature
202             sub drop_feature {
203             my $self = shift;
204             my $err = 0;
205             for my $drop (@_) {
206             # Remove references to this feature
207             $self->drop_child($drop, $self->children($drop)) or $err = 1;
208             $self->drop_parent($drop, $self->parents($drop)) or $err = 1;
209              
210             # Remove the feature itself
211             delete $self->{$drop};
212             }
213             return $err ? () : 1;
214             }
215              
216             sub all_features {
217             return %{$_[0]};
218             }
219              
220             # Get array of children
221             sub children {
222             my ($self, $feature) = @_;
223             return exists $self->{$feature} ?
224             keys %{$self->{$feature}->{child}} :
225             err "No such feature '$feature'";
226             }
227              
228             # Add a new child to a parent
229             sub add_child {
230             my ($self, $parent, @children) = @_;
231             my $err = 0;
232              
233             # Check that parent exists
234             $self->feature($parent) or return;
235              
236             CHILD: for my $child (@children) {
237             # Check that the child feature exists
238             if (not $self->feature($child)) {
239             $err = 1;
240             next CHILD;
241             }
242              
243             # Mark relations on parents and children
244             $self->{$parent}->{child}->{$child} = undef;
245             $self->{$child}->{parent}->{$parent} = undef;
246             }
247              
248             return $err ? (): 1;
249             }
250              
251             # Get rid of a child
252             sub drop_child {
253             my ($self, $parent, @children) = @_;
254             my $err = 0;
255              
256             # Check that parent exists
257             $self->feature($parent) or return;
258              
259             CHILD: for my $child (@children) {
260             # Check that the child exists
261             if (not $self->feature($child)) {
262             $err = 1;
263             next CHILD;
264             }
265              
266             # Remove marks
267             delete $self->{$parent}->{child}->{$child};
268             delete $self->{$child}->{parent}->{$parent};
269             }
270              
271             return $err ? () : 1;
272             }
273              
274             # Get current parents
275             sub parents {
276             my ($self, $feature) = @_;
277             return exists $self->{$feature} ?
278             keys %{$self->{$feature}->{parent}} :
279             err "No such feature '$feature'";
280             }
281              
282             # Add a parent
283             sub add_parent {
284             my ($self, $child, @parents) = @_;
285             my $err = 0;
286              
287             # Check that the child exists
288             $self->feature($child) or return;
289              
290             # This action is identical to add_child, but with order of arguments switched
291             # So just pass the buck
292             for (@parents) {
293             $self->add_child($_, $child) or $err = 1;
294             }
295             return $err ? () : 1;
296             }
297              
298             # Get rid of a parent
299             sub drop_parent {
300             my ($self, $child, @parents) = @_;
301             my $err = 0;
302              
303             # Child exists?
304             $self->feature($child) or return;
305              
306             # Once again, just pass to drop_child
307             for (@parents) {
308             $self->drop_child($_, $child) or $err = 1;
309             }
310              
311             return $err ? () : 1;
312             }
313              
314             # Get/set feature type
315             sub type {
316             my ($self, $feature, $type) = @_;
317             $self->feature($feature) or return;
318              
319             if ($type) {
320             # Check for valid types
321             return err("Invalid type $type") if (not $valid{$type});
322              
323             # Otherwise:
324             $self->{$feature}->{type} = $type;
325             }
326            
327             # Return the current type
328             $self->{$feature}->{type};
329             }
330              
331             # Load feature definitions from a file
332             sub loadfile {
333             my ($self, $file) = @_;
334              
335             my $parse;
336             # Load defaults when no file given
337             if (not defined $file) {
338             my $start = tell DATA;
339             my $string = join '', ;
340             eval { $parse = _parse_from_string($string, 'features') };
341             return err($@) if $@;
342             seek DATA, $start, 0;
343             }
344              
345             # Load an actual file
346             else {
347             eval { $parse = _parse_from_file($file, 'features') };
348             if (!$parse) {
349             return $self->old_loadfile($file);
350             }
351             }
352              
353             $self->_load_from_struct($parse);
354             }
355              
356             # The parser for the old deprecated format
357             sub old_loadfile {
358             my ($self, $file) = @_;
359              
360             eval { $file = _to_handle($file, '<') };
361             return err($@) if $@;
362              
363             my %children;
364             while (<$file>) {
365             s/\#.*$//; # Strip comments
366             if (/^\s*(\w+)\t+(\w+)(\t+(.*))?/) {
367             my ($name, $type, $children) = ($1, $2, $4);
368             no warnings 'uninitialized';
369             $self->add_feature($name => {type => $type});
370             $children{$name} = [ split /\s+/, $children ] if $children;
371             }
372              
373             }
374              
375             # Add kids
376             $self->add_child($_, @{$children{$_}}) for keys %children;
377              
378             close $file;
379             return 1;
380             }
381              
382             # Actually apply a structure to the object. Private, called by loadfile() and
383             # Lingua::Phonology
384             sub _load_from_struct {
385             my ($self, $parse) = @_;
386              
387             # This line is perhaps too clever for its own good
388             my %children = map { $_ => delete($parse->{$_}->{child}) } keys %$parse;
389             my %parents = map { $_ => delete($parse->{$_}->{parent}) } keys %$parse;
390              
391             $self->add_feature(%$parse);
392             $self->add_child($_, keys %{$children{$_}}) for keys %children;
393             $self->add_parent($_, keys %{$parents{$_}}) for keys %parents;
394             1;
395             }
396              
397             # Return an XML representation of yourself
398             sub _to_str {
399             my $self = shift;
400              
401             # Construct an appropriate data structure
402             my $struct = {};
403             for (keys %$self) {
404             # Only make child attrs, not parent attrs
405             $struct->{$_}->{child} = [ map { { name => $_ } } keys %{$self->{$_}->{child}} ];
406             $struct->{$_}->{type} = $self->{$_}->{type};
407             }
408              
409             return eval { _string_from_struct({ features => { feature => $struct } }) };
410             }
411              
412             # The following coderefs translate arbitrary data into numeric equivalents
413             # respecting common linguistic abbreviations like [+foo, -bar, *baz]
414             my %num_form = (
415             privative => sub {
416             return 1 if $_[0];
417             return undef;
418             },
419             binary =>sub {
420             my $value = shift;
421             # Text values
422             return 0 if ($value eq '-');
423             return 1 if ($value eq '+');
424             # Other values
425             return 1 if ($value);
426             return 0;
427             },
428             scalar => sub {
429             return $_[0]; # Nothing happens to scalars
430             }
431             );
432              
433             # Translate our input (presumably text) into a number
434             sub number_form {
435             my $self = shift;
436              
437             return err("Not enough arguments to number_form") if (@_ < 2);
438             my ($feature, $value) = @_;
439            
440             my $ref = $self->feature($feature) or return;
441              
442             # undef is always valid
443             # '*' is always a synonym for undef
444             return undef if (not defined($value));
445             return undef if ($value eq '*');
446              
447             # Otherwise, pass processing to the appropriate coderef
448             return $num_form{$ref->{type}}->($value);
449             }
450              
451             # These coderefs take numeric data and return their text equivs (inverse of
452             # %num_form)
453             my %text_form = (
454             privative => sub {
455             return '';
456             },
457             binary => sub {
458             return '+' if shift;
459             return '-';
460             },
461             scalar => sub {
462             return shift;
463             },
464             );
465              
466             sub text_form {
467             my $self = shift;
468              
469             return err("Not enough arguments to text_form") if (@_ < 2);
470             my ($feature, $value) = @_;
471            
472             my $ref = $self->feature($feature) or return;
473              
474             # first mash through number_form
475             $value = $self->number_form($feature, $value);
476              
477             # '*' is always a synonym for undef
478             return '*' if (not defined($value));
479              
480             return $text_form{$ref->{type}}->($value);
481             }
482              
483             1;
484              
485             =head1 METHODS
486              
487             =head2 new
488              
489             my $features = Lingua::Phonology::Features->new();
490              
491             This method creates and returns a new Features object. It takes no arguments.
492              
493             =head2 add_feature
494              
495             Adds a new feature to the current list of features. Accepts a list of
496             arguments of the form "feature_name => { ... }", where the value assigned
497             to each feature name must be a hash reference with one or more of the
498             following keys:
499              
500             =over 4
501              
502             =item * type
503              
504             The type must be one of 'privative', 'binary', 'scalar', or 'node'.
505             The feature created is of the type specified. This key must be defined for all
506             features. As of version 0.3, the 'node' type is deprecated, and is considered
507             synonymous with 'privative'.
508              
509             =item * child
510              
511             The value for this key is a reference to an array of feature names.
512             The features named will be assigned as the children of the feature being
513             defined. Note that any type of feature may have children, and children may be
514             of any type. (This is new in version 0.3.)
515              
516             =item * parent
517              
518             The inverse of C. The value for this key must be a
519             reference to an array of feature names that are assigned as the parents
520             of the feature being added.
521              
522             =back
523              
524             Note that the features named in C or C must already be
525             defined when the new feature is added. Thus, trying to add parents and
526             children as part of the same call to C will almost certainly
527             result in errors.
528              
529             This method return true on success and false if any error occurred.
530              
531             Example:
532              
533             $features->add_feature(
534             anterior => { type => 'binary' },
535             distributed => { type => 'binary' }
536             );
537             $features->add_feature(
538             Coronal => { type => 'privative', child => ['anterior', 'distributed']}
539             );
540              
541             Note that if you attempt to add a feature that already exists, the preexisting
542             feature will be dropped before the new feature is added.
543              
544             B: The features C are used by
545             Lingua::Phonology::Syllable. They may be defined as part of a user feature set
546             if you insist, but their original definitions may be overwritten, since
547             Lingua::Phonology::Syllable will forcibly redefine those features when it is
548             used. You have been warned.
549              
550             =head2 feature
551              
552             my $feature = $features->feature('name');
553              
554             Given the name of a feature, returns a hash reference showing the current
555             settings for that feature. The hash reference will at least contain the key
556             C, naming the feature type, and may contain the keys C and/or
557             C if the feature has some children or parents. If you ask for a feature
558             that doesn't exist, this method will return undef and emit a warning.
559              
560             =head2 feature_exists
561              
562             my $bool = $features->feature_exists('name');
563              
564             Given the name of the feature, returns a simple truth value indicating whether
565             or not any such feature with that name currently exists. Unlike C,
566             this method never gives an error, and does not return the feature reference on
567             success. This can be used by programs that want to quickly check for the
568             existence of a feature without printing warnings.
569              
570             =head2 all_features
571              
572             my %features = $features->all_features();
573              
574             Takes no arguments. Returns a hash with feature names as its keys, and the
575             parameters for those features as its values. The values will be hash references
576             the same as those returned from C;
577              
578             =head2 drop_feature
579              
580             $features->drop_feature('name');
581             $features->drop_feature(@names);
582              
583             Given one or more feature names, deletes the given feature(s) from the current
584             list of features. Note that deleting a feature does not cause its children to
585             be deleted--it just causes them to revert to being undominated. This method
586             returns true on success, otherwise false with an error.
587              
588             =head2 change_feature
589              
590             This method works identically to add_feature(), but it first checks to see that
591             the feature being changed already exists. If it doesn't, it will give an error.
592             If there are no errors, the method returns true.
593              
594             The C method can also be used to change existing features. Using
595             C, however, allows you to modify an existing feature without
596             losing existing settings for that feature. For example, consider the following:
597              
598             $features->add_feature(foo => { type => 'privative', child => ['bar', 'baz'] });
599             $features->change_feature(foo => { type => 'scalar' });
600             # foo is still the parent of bar and baz
601              
602             If C had been used in place of C, C would
603             not be the parent of anything, because the original settings for its children
604             would have been lost.
605              
606             =head2 children
607              
608             my @children = $features->children('name');
609              
610             Takes one argument, the name of a feature. Returns a list of all of the
611             features that are children of the feature given.
612              
613             =head2 add_child
614              
615             $features->add_child('parent', 'child');
616             $features->add_child('parent', @children);
617              
618             Takes two or more arguments. The first argument to this method should be the
619             name of a feature. The remaining arguments are the names of features to be
620             assigned as children to the first feature. If all children are added without
621             errors, this function returns true, otherwise false with a warning.
622              
623             =head2 drop_child
624              
625             $features->drop_child('parent', 'child');
626             $features->drop_child('parent', @children);
627              
628             Like add_child, the first argument to this function should be the name of a
629             feature, and the remaining arguments are the names of children of that feature.
630             The child features so named will be deleted from the list of children for that
631             node. This function returns true on success, false w/ a warning on any error.
632              
633             =head2 parents
634              
635             my @parents = $features->parents('name');
636              
637             Takes one argument, the name of a feature. Returns a list of the current
638             parent features of that feature.
639              
640             =head2 add_parent
641              
642             $features->add_parent('child', 'parent');
643             $features->add_parent('child', @parents);
644              
645             Takes two or more arguments. The first argument is the name of a feature, and
646             the remaining arguments are the names of features that should be parents of
647             that feature. Returns true if all of the attempted operations succeeded,
648             otherwise returns false.
649              
650             =head2 drop_parent
651              
652             $features->drop_parent('child', 'parent');
653             $features->drop_parent('child', @parents);
654              
655             Takes two or more arguments. The first is a feature name, and the remaining
656             arguments are the names of features that are currently parents of that feature.
657             Those features will cease to be parents of the first feature. Returns true on
658             success, false on error.
659              
660             =head2 type
661              
662             # Get a feature type
663             $features->type('name');
664             # Set a feature's type to 'binary', for example
665             $features->type('name', 'binary');
666              
667             Takes one or two arguments. The first argument must be the name of a
668             feature. If there is only one argument, the type for that feature is
669             return. If there are two arguments, the type is set to the second
670             argument and returned.
671              
672             =head2 loadfile
673              
674             # Load defaults
675             $features->loadfile();
676            
677             # Load from a file
678             $features->loadfile('phono.xml');
679              
680             Takes one argument, the path and name of a file. Reads the lines of the file
681             and adds all of the features defined therein. The file should be an XML file
682             following the format described in L. Consult
683             that module if you need to write an appropriate file by hand.
684              
685             You can also call this method with no arguments, in which case the default
686             feature set is loaded. The default set is described in L<"THE DEFAULT FEATURE
687             SET">.
688              
689             If this method is unable to parse its input as an XML file, it will then pass
690             the file off to C, where it will attempt to parse the the file
691             according to the old, deprecated file format. If you have an existing script
692             that loads a file in the old file format with C, there's nothing
693             that needs to be done immediately since the file will still be parsed
694             correctly. However, you will get warnings telling you that the format you're
695             using is deprecated.
696              
697             =head2 old_loadfile
698              
699             $features->old_loadfile('filename');
700              
701             Loads a file in the old (pre-version 0.2) and currently deprecated file format.
702             This format is described below.
703              
704             Feature definition lines should be in this format:
705              
706             feature_name [1 or more tabs] type [1 or more tabs] children (separated by spaces)
707              
708             You can order your features any way you want in the file. The method will
709             take care of ensuring that parents are defined before their children are
710             added and make sure no conflicts result.
711              
712             Lines beginning with a '#' are assumed to be comments and are skipped.
713              
714             This method does NOT load the default features any more. Only C
715             does that.
716              
717             =head2 number_form
718              
719             my $num = $features->number_form('name', $text);
720              
721             Takes two arguments. The first argument is the name of a feature, and the
722             second is a value to be converted into the appropriate numeric format for that
723             feature. This function is provided for convenience, to allow Lingua::Phonology
724             to convert between common textual linguistic notation and its internal numeric
725             representation.
726              
727             The conversion from input value to numeric value depends on what type of
728             feature the feature given in the first argument is. A few general text
729             conventions are recognized to make text parsing easier and to ensure that
730             number_form and L<"text_form"> can be used as inverses of each other. The
731             conversions are as follows:
732              
733             =over 4
734              
735             =item * privatives
736              
737             The string '*' is recognized as a synonym for C in all circumstances.
738             It always returns C.
739              
740             =item *
741              
742             B features return 1 if given any true true value (other than '*'),
743             otherwise C.
744              
745             =item *
746              
747             B features return 1 in the case of a true value, 0 in case of a
748             defined false value, and otherwise C. The string '+' is a synonym for
749             1, and '-' is a synonym for 0. Thus, the following two lines both return
750             0:
751              
752             print $features->number_form('binary_feature', 0); # prints 0
753             print $features->number_form('binary_feature', '-'); # prints 0
754              
755             Note, however, if the feature given is a privative feature, the first returns
756             C and the second returns 1.
757              
758             =item *
759              
760             B features return the value that they're given unchanged (unless
761             that value is '*', which is translated to C).
762              
763             =item *
764              
765             =back
766              
767              
768             =head2 text_form
769              
770             my $text = $features->text_form('name', $number);
771              
772             This function is the inverse of number_form. It takes two arguments, a
773             feature name and a numeric value, and returns a text equivalent for the
774             numeric value given. The exact translation depends on the type of the
775             feature given in the first argument. The translations are:
776              
777             =over 4
778              
779             =item *
780              
781             Any undefined value or the string '*' returns '*'.
782              
783             =item *
784              
785             B features return '*' for undef or logically false values, otherwise
786             '' (an empty string).
787              
788             =item *
789              
790             B features return '+' if true, '-' if false or equal to '-', and '*' if
791             undefined.
792              
793             =item *
794              
795             B features return their values unchanged, except if they're undefined,
796             in which case they return '*'.
797              
798             =item *
799              
800             =back
801              
802             =head1 THE DEFAULT FEATURE SET
803              
804             If you call the method C> without any arguments, like this:
805              
806             $features->loadfile
807              
808             then the default feature set is loaded. The default feature set is a
809             feature geometry tree based on Clements and Hume (1995), with some
810             modifications. This set gratuitously mixes privative, binary, and scalar
811             features, and may or may not be actually useful to you.
812              
813             Within this feature set, we use the convention of putting top-level
814             (undominated) nodes in ALL CAPS, putting intermediate nodes in Initial Caps,
815             and putting terminal features in lowercase. The following shows the feature
816             tree created, with the types of each feature in parenthesis:
817              
818             # True features
819             ROOT (privative)
820             |
821             +-sonorant (privative)
822             +-approximant (privative)
823             +-vocoid (privative)
824             +-nasal (privative)
825             +-lateral (privative)
826             +-continuant (binary)
827             +-Laryngeal
828             | |
829             | +-spread (privative)
830             | +-constricted (privative)
831             | +-voice (privative)
832             | +-ATR (binary)
833             |
834             +-Place
835             |
836             +-pharyngeal (privative)
837             +-Oral
838             |
839             +-labial (privative)
840             +-Lingual
841             | |
842             | +-dorsal (privative)
843             | +-Coronal
844             | |
845             | +-anterior (binary)
846             | +-distributed (binary)
847             |
848             +-Vocalic
849             |
850             +-aperture (scalar)
851             +-tense (privative)
852             +-Vplace
853             |
854             +-labial (same as above)
855             +-Lingual (same as above)
856            
857             # Features dealing with syllable structure
858             SYLL (privative)
859             |
860             +-onset (privative)
861             +-Rime (privative)
862             |
863             +-nucleus (privative)
864             +-coda (privative)
865             SON (scalar)
866              
867             This feature set is created from the following XML file, which can be treated
868             as an example for creating your own feature sets.
869              
870            
871            
872              
873            
874              
875            
876            
877            
878            
879            
880            
881            
882            
883            
884            
885            
886            
887            
888            
889            
890            
891              
892            
893            
894            
895            
896            
897            
898            
899            
900            
901            
902              
903            
904            
905            
906            
907            
908              
909            
910            
911            
912            
913            
914            
915              
916            
917            
918            
919            
920            
921              
922            
923            
924            
925            
926            
927            
928              
929            
930            
931            
932            
933            
934            
935            
936              
937            
938            
939            
940            
941              
942            
943              
944            
945            
946            
947            
948              
949            
950            
951            
952            
953            
954            
955            
956              
957            
958              
959            
960            
961              
962             =head1 TO DO
963              
964             Improve the default feature set. As it is, it cannot handle uvulars or
965             pharyngeals, and has some quirks in its relationships that lead to strange
966             results. Though some of this is the fault of phonologists who can't make up
967             their minds about how things are supposed to go together.
968              
969             =head1 SEE ALSO
970              
971             L
972              
973             L
974              
975             =head1 REFERENCES
976              
977             Clements, G.N and E. Hume. "The Internal Organization of Speech Sounds."
978             Handbook of Phonological Theory. Ed. John A. Goldsmith. Cambridge,
979             Massachusetts: Blackwell, 2001. 245-306.
980              
981             This article is a terrific introduction to the concept of feature geometry,
982             and also describes ways to write rules in a feature-geometric system.
983              
984             =head1 AUTHOR
985              
986             Jesse S. Bangs >
987              
988             =head1 LICENSE
989              
990             This module is free software. You can distribute and/or modify it under the
991             same terms as Perl itself.
992              
993             =cut
994              
995             __DATA__