phonology 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README.md +5 -0
- data/lib/phonology/features.rb +182 -0
- data/lib/phonology/sound.rb +103 -0
- data/lib/phonology/sounds.rb +69 -0
- data/lib/phonology/version.rb +3 -0
- data/lib/phonology.rb +22 -0
- data/test/features_test.rb +10 -0
- data/test/phonology_test.rb +17 -0
- data/test/sound_test.rb +84 -0
- data/test/sounds_test.rb +32 -0
- data/test/test_helper.rb +9 -0
- metadata +73 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Norman Clarke
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
module Phonology
|
2
|
+
|
3
|
+
# Raised for any attempt to access an invalid feature.
|
4
|
+
class FeatureError < StandardError; end
|
5
|
+
|
6
|
+
# Distinctive features.
|
7
|
+
module Features
|
8
|
+
|
9
|
+
# Pulmonic consonants
|
10
|
+
PULMONIC = [:nasal, :plosive, :fricative, :approximant, :trill, :flap,
|
11
|
+
:lateral, :bilabial, :labiodental, :dental, :alveolar, :postalveolar,
|
12
|
+
:retroflex, :palatal, :velar, :uvular, :pharyngeal, :epiglottal, :glottal]
|
13
|
+
|
14
|
+
# Non-pulmonic consonants
|
15
|
+
NON_PULMONIC = [:click, :implosive, :ejective]
|
16
|
+
|
17
|
+
# Vocalic features
|
18
|
+
VOCALIC = [:close, :near_close, :close_mid, :mid, :open_mid, :near_open, :open,
|
19
|
+
:front, :near_front, :central, :near_back, :back, :rounded]
|
20
|
+
|
21
|
+
# Consontal features
|
22
|
+
CONSONANTAL = (PULMONIC + NON_PULMONIC).to_set
|
23
|
+
|
24
|
+
# All features classes
|
25
|
+
ALL = ([:voiced] + PULMONIC + NON_PULMONIC + VOCALIC).to_set
|
26
|
+
|
27
|
+
# Manner of articulation features
|
28
|
+
MANNER = [:nasal, :plosive, :fricative, :approximant, :trill, :flap, :lateral] + NON_PULMONIC
|
29
|
+
|
30
|
+
# Place of articulation features
|
31
|
+
PLACE = CONSONANTAL.to_a - MANNER
|
32
|
+
|
33
|
+
# Vowel height
|
34
|
+
HEIGHT = [:close, :near_close, :close_mid, :mid, :open_mid, :near_open, :open]
|
35
|
+
|
36
|
+
# Vowel backness
|
37
|
+
BACKNESS = (VOCALIC.to_set - HEIGHT - [:rounded])
|
38
|
+
|
39
|
+
# Feature classes
|
40
|
+
CLASSES = {
|
41
|
+
:labial => [:bilabial, :labiodental].to_set,
|
42
|
+
:coronal => [:dental, :alveolar, :postalveolar, :retroflex].to_set,
|
43
|
+
:dorsal => [:palatal, :velar, :uvular].to_set,
|
44
|
+
:radical => [:pharyngeal, :epiglottal].to_set,
|
45
|
+
:vocalic => VOCALIC + [:rounded],
|
46
|
+
:non_pulmonic => NON_PULMONIC,
|
47
|
+
:pulmonic => PULMONIC,
|
48
|
+
:consonantal => CONSONANTAL
|
49
|
+
}
|
50
|
+
|
51
|
+
# Sets of distinctive features mapped to an IPA symbol
|
52
|
+
SETS = {
|
53
|
+
[:voiced, :bilabial, :nasal].to_set => [0x006d],
|
54
|
+
[:voiced, :labiodental, :nasal].to_set => [0x0271],
|
55
|
+
[:voiced, :alveolar, :nasal].to_set => [0x006e],
|
56
|
+
[:voiced, :retroflex, :nasal].to_set => [0x0273],
|
57
|
+
[:voiced, :palatal, :nasal].to_set => [0x0272],
|
58
|
+
[:voiced, :velar, :nasal].to_set => [0x014b],
|
59
|
+
[:voiced, :uvular, :nasal].to_set => [0x0274],
|
60
|
+
[:bilabial, :plosive].to_set => [0x0070],
|
61
|
+
[:voiced, :bilabial, :plosive].to_set => [0x0062],
|
62
|
+
[:labiodental, :plosive].to_set => [0x0070, 0x032a],
|
63
|
+
[:voiced, :labiodental, :plosive].to_set => [0x0062, 0x032a],
|
64
|
+
[:alveolar, :plosive].to_set => [0x0074],
|
65
|
+
[:voiced, :alveolar, :plosive].to_set => [0x0064],
|
66
|
+
[:retroflex, :plosive].to_set => [0x0288],
|
67
|
+
[:voiced, :retroflex, :plosive].to_set => [0x0256],
|
68
|
+
[:palatal, :plosive].to_set => [0x0063],
|
69
|
+
[:voiced, :palatal, :plosive].to_set => [0x025f],
|
70
|
+
[:velar, :plosive].to_set => [0x006b],
|
71
|
+
[:voiced, :velar, :plosive].to_set => [0x0261],
|
72
|
+
[:uvular, :plosive].to_set => [0x0071],
|
73
|
+
[:voiced, :uvular, :plosive].to_set => [0x0262],
|
74
|
+
[:voiced, :epiglottal, :plosive].to_set => [0x02a1],
|
75
|
+
[:voiced, :glottal, :plosive].to_set => [0x0294],
|
76
|
+
[:bilabial, :fricative].to_set => [0x0278],
|
77
|
+
[:voiced, :bilabial, :fricative].to_set => [0x03b2],
|
78
|
+
[:labiodental, :fricative].to_set => [0x0066],
|
79
|
+
[:voiced, :labiodental, :fricative].to_set => [0x0076],
|
80
|
+
[:dental, :fricative].to_set => [0x03b8],
|
81
|
+
[:voiced, :dental, :fricative].to_set => [0x00f0],
|
82
|
+
[:alveolar, :fricative].to_set => [0x0073],
|
83
|
+
[:voiced, :alveolar, :fricative].to_set => [0x007a],
|
84
|
+
[:postalveolar, :fricative].to_set => [0x0283],
|
85
|
+
[:voiced, :postalveolar, :fricative].to_set => [0x0292],
|
86
|
+
[:retroflex, :fricative].to_set => [0x0282],
|
87
|
+
[:voiced, :retroflex, :fricative].to_set => [0x0290],
|
88
|
+
[:palatal, :fricative].to_set => [0x00e7],
|
89
|
+
[:voiced, :palatal, :fricative].to_set => [0x029d],
|
90
|
+
[:velar, :fricative].to_set => [0x0078],
|
91
|
+
[:voiced, :velar, :fricative].to_set => [0x0263],
|
92
|
+
[:uvular, :fricative].to_set => [0x03c7],
|
93
|
+
[:voiced, :uvular, :fricative, :approximant].to_set => [0x0281],
|
94
|
+
[:pharyngeal, :fricative].to_set => [0x0127],
|
95
|
+
[:voiced, :pharyngeal, :fricative, :approximant].to_set => [0x0295],
|
96
|
+
[:epiglottal, :fricative].to_set => [0x029c],
|
97
|
+
[:voiced, :epiglottal, :fricative, :approximant].to_set => [0x02a2],
|
98
|
+
[:glottal, :fricative, :approximant].to_set => [0x0068],
|
99
|
+
[:voiced, :glottal, :fricative, :approximant].to_set => [0x0266],
|
100
|
+
[:voiced, :bilabial, :approximant].to_set => [0x03b2, 0x031e],
|
101
|
+
[:voiced, :labiodental, :approximant].to_set => [0x028b],
|
102
|
+
[:voiced, :alveolar, :approximant].to_set => [0x0279],
|
103
|
+
[:voiced, :retroflex, :approximant].to_set => [0x027b],
|
104
|
+
[:voiced, :palatal, :approximant].to_set => [0x006a],
|
105
|
+
[:voiced, :velar, :approximant].to_set => [0x0270],
|
106
|
+
[:voiced, :bilabial, :trill].to_set => [0x0299],
|
107
|
+
[:voiced, :alveolar, :trill].to_set => [0x0072],
|
108
|
+
[:voiced, :uvular, :trill].to_set => [0x0280],
|
109
|
+
[:voiced, :epiglottal, :trill].to_set => [0x044f],
|
110
|
+
[:voiced, :bilabial, :flap].to_set => [0x2c71, 0x031f],
|
111
|
+
[:voiced, :labiodental, :flap].to_set => [0x2c71],
|
112
|
+
[:voiced, :alveolar, :flap].to_set => [0x027e],
|
113
|
+
[:voiced, :retroflex, :flap].to_set => [0x027d],
|
114
|
+
[:voiced, :uvular, :flap].to_set => [0x0262, 0x0306],
|
115
|
+
[:voiced, :epiglottal, :flap].to_set => [0x02a1, 0x032f],
|
116
|
+
[:alveolar, :lateral, :fricative].to_set => [0x026c],
|
117
|
+
[:voiced, :alveolar, :lateral, :fricative].to_set => [0x026e],
|
118
|
+
[:voiced, :alveolar, :lateral, :approximant].to_set => [0x006c],
|
119
|
+
[:voiced, :retroflex, :lateral, :approximant].to_set => [0x026d],
|
120
|
+
[:voiced, :palatal, :lateral, :approximant].to_set => [0x028e],
|
121
|
+
[:voiced, :velar, :lateral, :approximant].to_set => [0x029f],
|
122
|
+
[:voiced, :alveolar, :lateral, :flap].to_set => [0x027a],
|
123
|
+
[:voiced, :palatal, :lateral, :flap].to_set => [0x028e, 0x032f],
|
124
|
+
[:voiced, :velar, :lateral, :flap].to_set => [0x029f, 0x0306],
|
125
|
+
[:voiced, :close, :front].to_set => [0x0069],
|
126
|
+
[:voiced, :close, :front, :rounded].to_set => [0x0079],
|
127
|
+
[:voiced, :close, :central].to_set => [0x0268],
|
128
|
+
[:voiced, :close, :central, :rounded].to_set => [0x0289],
|
129
|
+
[:voiced, :close, :back].to_set => [0x026f],
|
130
|
+
[:voiced, :close, :back, :rounded].to_set => [0x0075],
|
131
|
+
[:voiced, :near_close, :near_front].to_set => [0x026a],
|
132
|
+
[:voiced, :near_close, :near_front, :rounded].to_set => [0x028f],
|
133
|
+
[:voiced, :near_close, :near_back, :rounded].to_set => [0x028a],
|
134
|
+
[:voiced, :close_mid, :front].to_set => [0x0065],
|
135
|
+
[:voiced, :close_mid, :front, :rounded].to_set => [0x00f8],
|
136
|
+
[:voiced, :close_mid, :central].to_set => [0x0258],
|
137
|
+
[:voiced, :close_mid, :central, :rounded].to_set => [0x0275],
|
138
|
+
[:voiced, :close_mid, :back].to_set => [0x0264],
|
139
|
+
[:voiced, :close_mid, :back, :rounded].to_set => [0x006f],
|
140
|
+
[:voiced, :mid, :central].to_set => [0x0259],
|
141
|
+
[:voiced, :open_mid, :front].to_set => [0x025b],
|
142
|
+
[:voiced, :open_mid, :front, :rounded].to_set => [0x0153],
|
143
|
+
[:voiced, :open_mid, :central].to_set => [0x025c],
|
144
|
+
[:voiced, :open_mid, :central, :rounded].to_set => [0x025e],
|
145
|
+
[:voiced, :open_mid, :back].to_set => [0x028c],
|
146
|
+
[:voiced, :open_mid, :back, :rounded].to_set => [0x0254],
|
147
|
+
[:voiced, :near_open, :front].to_set => [0x00e6],
|
148
|
+
[:voiced, :near_open, :central].to_set => [0x0250],
|
149
|
+
[:voiced, :open, :front].to_set => [0x0061],
|
150
|
+
[:voiced, :open, :front, :rounded].to_set => [0x0276],
|
151
|
+
[:voiced, :open, :back].to_set => [0x0251],
|
152
|
+
[:voiced, :open, :back, :rounded].to_set => [0x0252],
|
153
|
+
# This char will appear in Unicode 6.0
|
154
|
+
[:voiced, :retroflex, :lateral, :fricative].to_set => [0xa78e],
|
155
|
+
# Not in UTF-8; used in SIL International's Charis/Doulos font's private
|
156
|
+
# use area
|
157
|
+
[:voiced, :palatal, :lateral, :fricative].to_set => [0xf267],
|
158
|
+
[:voiced, :velar, :lateral, :fricative].to_set => [0xf268],
|
159
|
+
[:voiced, :retroflex, :lateral, :flap].to_set => [0xf269]
|
160
|
+
}
|
161
|
+
|
162
|
+
class << self
|
163
|
+
# Return the set corresponding to the kind of feature: manner or place of
|
164
|
+
# articulation, or height or backness.
|
165
|
+
def set(feature)
|
166
|
+
[MANNER, PLACE, BACKNESS, HEIGHT].each {|kind| return kind if kind.include? feature}
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
|
170
|
+
# Given a Ruby :symbol feature class such as :coronal, return the
|
171
|
+
# corresponding features. If a single feature is given, just return the
|
172
|
+
# feature.
|
173
|
+
def expand(*args)
|
174
|
+
args = args.inject([].to_set) do |memo, obj|
|
175
|
+
memo << (CLASSES[obj] || obj)
|
176
|
+
end.flatten.to_set
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Phonology
|
2
|
+
|
3
|
+
# A set of distinctive features
|
4
|
+
class Sound
|
5
|
+
|
6
|
+
attr_reader :features
|
7
|
+
|
8
|
+
def initialize(*features)
|
9
|
+
@features = if features.first.kind_of?(String)
|
10
|
+
Phonology.features(features.shift)
|
11
|
+
else
|
12
|
+
features.to_set
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Features::ALL.each do |feature|
|
17
|
+
class_eval(<<-EOM, __FILE__, __LINE__ +1)
|
18
|
+
def #{feature}?
|
19
|
+
@features.include? :#{feature}
|
20
|
+
end
|
21
|
+
EOM
|
22
|
+
end
|
23
|
+
|
24
|
+
Features::CLASSES.each do |feature_class, values|
|
25
|
+
class_eval(<<-EOM, __FILE__, __LINE__ +1)
|
26
|
+
def #{feature_class}?
|
27
|
+
set = Features.expand(:#{feature_class})
|
28
|
+
!set.intersection(@features).empty?
|
29
|
+
end
|
30
|
+
EOM
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the IPA symbol for the sound.
|
34
|
+
def symbol
|
35
|
+
Phonology.symbol(features)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add a feature, replacing either the place or manner of articulation,
|
39
|
+
# or the height or backness. Returns self.
|
40
|
+
def <<(feature)
|
41
|
+
feature = feature.to_sym
|
42
|
+
(@features -= (Features.set(feature) || [])) << feature
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias add <<
|
46
|
+
|
47
|
+
# Remove a feature, and return self.
|
48
|
+
def >>(feature)
|
49
|
+
@features -= [feature.to_sym]
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get the next sound, moving backwards in the mouth.
|
54
|
+
def backward
|
55
|
+
if consonantal?
|
56
|
+
index = Features::PLACE.index(place.first)
|
57
|
+
max = Features::PLACE.length
|
58
|
+
features = @features - Features::PLACE
|
59
|
+
until index > max do
|
60
|
+
index = index + 1
|
61
|
+
feature = Features::PLACE[index]
|
62
|
+
if self.class.exists?(features + [feature])
|
63
|
+
@features = features + [feature]
|
64
|
+
return self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def forward
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get the sound's place of articulation.
|
75
|
+
def place
|
76
|
+
@features.intersection(Features::PLACE)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the sound's manner of articulation.
|
80
|
+
def manner
|
81
|
+
@features.intersection(Features::MANNER)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the sound's height.
|
85
|
+
def height
|
86
|
+
@features.intersection(Features::HEIGHT)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the sound's backness.
|
90
|
+
def backness
|
91
|
+
@features.intersection(Features::BACKNESS)
|
92
|
+
end
|
93
|
+
|
94
|
+
def exists?
|
95
|
+
self.class.exists?(@features)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.exists?(features)
|
99
|
+
!! Features::SETS[features]
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Phonology
|
2
|
+
|
3
|
+
# An inventory of Phonological feature sets.
|
4
|
+
class Sounds
|
5
|
+
|
6
|
+
# A Hash with a set of features as keys, and UTF-8 codepoints for IPA
|
7
|
+
# letters as values.
|
8
|
+
attr :sets
|
9
|
+
|
10
|
+
# Get an instance, building its set from a list IPA characters.
|
11
|
+
def self.from_ipa(*chars)
|
12
|
+
chars = chars.flatten.map {|char| char.unpack("U*")}
|
13
|
+
new Features::SETS.select {|key, val| chars.include? val}
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(sets)
|
17
|
+
@sets = sets
|
18
|
+
end
|
19
|
+
|
20
|
+
# Given an IPA symbol, return a corresponding set of distinctive features.
|
21
|
+
def features(symbol)
|
22
|
+
@sets.key(symbol.unpack("U*"))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Given a set of features, return an IPA symbol
|
26
|
+
def symbol(*features)
|
27
|
+
codepoints(features).pack("U*")
|
28
|
+
end
|
29
|
+
|
30
|
+
def symbols
|
31
|
+
@sets.values.map {|symbol| symbol.pack("U*")}.to_set
|
32
|
+
end
|
33
|
+
|
34
|
+
# Given a set of features, return an array of UTF-8 codepoints.
|
35
|
+
def codepoints(features)
|
36
|
+
features = features.to_set.flatten
|
37
|
+
@sets[features] || (raise FeatureError, "No such set #{features.inspect}")
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return an instance of Sounds whose sets include any of the given
|
41
|
+
# features.
|
42
|
+
def with(*features)
|
43
|
+
features = features.to_set
|
44
|
+
self.class.new @sets.select {|key, val| !key.intersection(features).empty?}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return feature sets that include all of the given features
|
48
|
+
def with_all(*features)
|
49
|
+
features = features.to_set
|
50
|
+
self.class.new @sets.select {|key, val| features.subset?(key)}
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return an instance of Sounds whose sets exclude any of the given
|
54
|
+
# features.
|
55
|
+
def without(*features)
|
56
|
+
features = features.to_set
|
57
|
+
self.class.new @sets.select {|key, val| !features.subset?(key)}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return an instance of Sounds whose sets exclude all of the given
|
61
|
+
# features.
|
62
|
+
def without_any(*features)
|
63
|
+
features = features.to_set
|
64
|
+
self.class.new @sets.select {|key, val| key.intersection(features).empty?}
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/lib/phonology.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "set"
|
2
|
+
require File.expand_path("../phonology/features", __FILE__)
|
3
|
+
require File.expand_path("../phonology/sounds", __FILE__)
|
4
|
+
require File.expand_path("../phonology/sound", __FILE__)
|
5
|
+
|
6
|
+
module Phonology
|
7
|
+
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def sounds
|
11
|
+
@sounds ||= Sounds.new Features::SETS
|
12
|
+
end
|
13
|
+
|
14
|
+
def symbol(*args)
|
15
|
+
sounds.symbol(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def features(*args)
|
19
|
+
sounds.features(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path("../test_helper", __FILE__)
|
2
|
+
|
3
|
+
class FeaturesTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
test "should expand feature groups" do
|
6
|
+
assert_equal [:dental, :alveolar, :postalveolar, :retroflex, :velar].to_set,
|
7
|
+
Phonology::Features.expand(:coronal, :dental, :velar)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path("../test_helper", __FILE__)
|
2
|
+
|
3
|
+
class PhonologyTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
test "sounds should return an instance of Sounds" do
|
6
|
+
assert_equal Phonology::Sounds, Phonology.sounds.class
|
7
|
+
end
|
8
|
+
|
9
|
+
test "should return feature set for symbol" do
|
10
|
+
assert_equal [:voiced, :bilabial, :nasal].to_set, Phonology.features("m")
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should return a symbol for a feature set" do
|
14
|
+
assert_equal "d", Phonology.symbol(:voiced, :alveolar, :plosive)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/test/sound_test.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path("../test_helper", __FILE__)
|
2
|
+
|
3
|
+
class SoundTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include Phonology
|
6
|
+
|
7
|
+
test "should return an IPA symbol" do
|
8
|
+
assert_equal "m", Sound.new(:voiced, :bilabial, :nasal).symbol
|
9
|
+
end
|
10
|
+
|
11
|
+
test "should instantiate from IPA symbol" do
|
12
|
+
assert_equal [:voiced, :bilabial, :nasal].to_set, Sound.new("m").features
|
13
|
+
end
|
14
|
+
|
15
|
+
test "should indicate individual features" do
|
16
|
+
assert Sound.new("n").nasal?
|
17
|
+
end
|
18
|
+
|
19
|
+
test "should indicate feature classes" do
|
20
|
+
assert Sound.new("n").coronal?
|
21
|
+
assert Sound.new("n").consonantal?
|
22
|
+
assert !Sound.new("n").vocalic?
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should add/remove voice" do
|
26
|
+
sound = Sound.new("p")
|
27
|
+
sound << :voiced
|
28
|
+
assert_equal "b", sound.symbol
|
29
|
+
sound >> :voiced
|
30
|
+
assert_equal "p", sound.symbol
|
31
|
+
end
|
32
|
+
|
33
|
+
test "should add/remove rounding" do
|
34
|
+
sound = Sound.new("a")
|
35
|
+
sound << :rounded
|
36
|
+
assert_equal "\u0276", sound.symbol
|
37
|
+
sound >> :rounded
|
38
|
+
assert_equal "a", sound.symbol
|
39
|
+
end
|
40
|
+
|
41
|
+
test "should change manner of articulation" do
|
42
|
+
sound = Sound.new("m")
|
43
|
+
sound << :plosive
|
44
|
+
assert_equal "b", sound.symbol
|
45
|
+
end
|
46
|
+
|
47
|
+
test "should change place of articulation" do
|
48
|
+
assert_equal "n", Sound.new("m").add(:alveolar).symbol
|
49
|
+
end
|
50
|
+
|
51
|
+
test "should change backness" do
|
52
|
+
assert_equal "u", Sound.new("y").add(:back).symbol
|
53
|
+
end
|
54
|
+
|
55
|
+
test "should change height" do
|
56
|
+
assert_equal "i", Sound.new("a").add(:close).symbol
|
57
|
+
end
|
58
|
+
|
59
|
+
test "should get place of articulation" do
|
60
|
+
assert_equal :alveolar, Sound.new("r").place.first
|
61
|
+
end
|
62
|
+
|
63
|
+
test "should get manner of articulation" do
|
64
|
+
assert_equal :trill, Sound.new("r").manner.first
|
65
|
+
end
|
66
|
+
|
67
|
+
test "should get height" do
|
68
|
+
assert_equal :close_mid, Sound.new("e").height.first
|
69
|
+
end
|
70
|
+
|
71
|
+
test "should get backness" do
|
72
|
+
assert_equal :front, Sound.new("e").backness.first
|
73
|
+
end
|
74
|
+
|
75
|
+
test "should indicate if sound exists" do
|
76
|
+
assert !Sound.new(:front, :plosive).exists?
|
77
|
+
assert Sound.new(:bilabial, :plosive).exists?
|
78
|
+
end
|
79
|
+
|
80
|
+
test "should move sounds backwards" do
|
81
|
+
assert_equal "n", Sound.new("m").backward.backward.symbol
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/test/sounds_test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path("../test_helper", __FILE__)
|
2
|
+
|
3
|
+
class SoundsTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@sounds = Phonology.sounds
|
7
|
+
end
|
8
|
+
|
9
|
+
def small_set
|
10
|
+
Phonology::Sounds.from_ipa("b", "p", "d", "t", "m", "n")
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should return feature set for symbol" do
|
14
|
+
assert_equal [:voiced, :bilabial, :nasal].to_set, @sounds.features("m")
|
15
|
+
end
|
16
|
+
|
17
|
+
test "#with should return a set of sets that include any of the given features" do
|
18
|
+
assert_equal ["p", "b", "m", "n"].to_set, small_set.with(:bilabial, :nasal).symbols
|
19
|
+
end
|
20
|
+
|
21
|
+
test "#without should return a set of sets that exclude any of the given features" do
|
22
|
+
assert_equal ["p", "b", "n", "d", "t"].to_set, small_set.without(:bilabial, :nasal).symbols
|
23
|
+
end
|
24
|
+
|
25
|
+
test "#with_all should return a set of sets that include any of the given features" do
|
26
|
+
assert_equal ["m"].to_set, small_set.with_all(:bilabial, :nasal).symbols
|
27
|
+
end
|
28
|
+
|
29
|
+
test "#without_any should return a set of sets that exclude all of the given features" do
|
30
|
+
assert_equal ["d", "t"].to_set, small_set.without_any(:bilabial, :nasal).symbols
|
31
|
+
end
|
32
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: phonology
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Norman Clarke
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-06 00:00:00 -03:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: A phonology library for Ruby.
|
22
|
+
email: norman@njclarke.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/phonology.rb
|
31
|
+
- lib/phonology/features.rb
|
32
|
+
- lib/phonology/sound.rb
|
33
|
+
- lib/phonology/sounds.rb
|
34
|
+
- lib/phonology/version.rb
|
35
|
+
- test/features_test.rb
|
36
|
+
- test/phonology_test.rb
|
37
|
+
- test/sound_test.rb
|
38
|
+
- test/sounds_test.rb
|
39
|
+
- test/test_helper.rb
|
40
|
+
- README.md
|
41
|
+
- LICENSE
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/norman/phonology
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: "[none]"
|
68
|
+
rubygems_version: 1.3.6
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Phonology library for Ruby
|
72
|
+
test_files: []
|
73
|
+
|