spanish 0.0.2 → 0.0.3
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/lib/spanish.rb +16 -4
- data/lib/spanish/orthography.rb +6 -10
- data/lib/spanish/phonology.rb +34 -2
- data/lib/spanish/syllable.rb +62 -103
- data/lib/spanish/version.rb +1 -1
- data/test/phonology_test.rb +4 -4
- data/test/syllabification_test.rb +14 -15
- data/test/test_helper.rb +3 -0
- metadata +7 -4
data/lib/spanish.rb
CHANGED
@@ -21,8 +21,12 @@ module Spanish
|
|
21
21
|
# Get an array of Phonology::Sounds from the string.
|
22
22
|
def get_sounds(string, *rules)
|
23
23
|
sequence = Orthography.translator.translate(string)
|
24
|
-
|
25
|
-
|
24
|
+
Syllabifier.syllabify(sequence)
|
25
|
+
Phonology.general_rules.values.each do |rule|
|
26
|
+
rule.apply(sequence)
|
27
|
+
end
|
28
|
+
rules.each do |rule|
|
29
|
+
Phonology.optional_rules[rule.to_sym].apply(sequence)
|
26
30
|
end
|
27
31
|
sequence
|
28
32
|
end
|
@@ -32,8 +36,16 @@ module Spanish
|
|
32
36
|
#
|
33
37
|
# Spanish.get_ipa("chavo")
|
34
38
|
# # => 't͡ʃaβo
|
35
|
-
def get_ipa(string)
|
36
|
-
get_sounds(string).to_s
|
39
|
+
def get_ipa(string, *rules)
|
40
|
+
get_sounds(string, *rules).to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_syllables(string, *rules)
|
44
|
+
get_sounds(string, *rules).map(&:syllable).uniq
|
37
45
|
end
|
38
46
|
|
47
|
+
def get_syllables_ipa(string, *rules)
|
48
|
+
syllables = get_syllables(string, *rules)
|
49
|
+
syllables.map {|s| syllables.length == 1 ? s.to_s(false) : s.to_s }.join(" ")
|
50
|
+
end
|
39
51
|
end
|
data/lib/spanish/orthography.rb
CHANGED
@@ -1,13 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Spanish
|
3
3
|
module Orthography
|
4
|
-
|
5
4
|
extend self
|
6
|
-
|
7
5
|
LETTERS = /ch|ll|ñ|á|é|í|ó|ú|ü|[\w]/
|
8
|
-
|
9
6
|
SCANNER = lambda {|string| string.downcase.scan(/rr|ch|ll|ñ|á|é|í|ó|ú|ü|\w/)}
|
10
|
-
|
11
7
|
SOUNDS = ::Phonology::Inventory.from_ipa(
|
12
8
|
"m", "n", "ɲ", "ŋ", "p", "b", "t", "d", "k", "ɡ", "β", "f", "v", "ð", "s",
|
13
9
|
"z", "ʒ", "ʝ", "x", "ɣ", "j", "r", "ɾ", "l", "w", "i", "u", "e", "o", "a",
|
@@ -78,9 +74,8 @@ module Spanish
|
|
78
74
|
if initial?
|
79
75
|
get(:unvoiced, :velar, :fricative)
|
80
76
|
else
|
81
|
-
[get(:unvoiced, :velar, :plosive), Phonology::Sound.new("s")]
|
77
|
+
[get(:unvoiced, :velar, :plosive), ::Phonology::Sound.new("s")]
|
82
78
|
end
|
83
|
-
|
84
79
|
when "y"
|
85
80
|
if initial? && final?
|
86
81
|
get(:close, :front)
|
@@ -93,7 +88,10 @@ module Spanish
|
|
93
88
|
elsif !precedes(vowel)
|
94
89
|
get(:palatal, :approximant)
|
95
90
|
end
|
96
|
-
when "z"
|
91
|
+
when "z"
|
92
|
+
# Spanish final "z" indicates stress: i.e. "madures" vs. "madurez"
|
93
|
+
last_sound.hint(:primary_stress) if final? and follows(vowel)
|
94
|
+
get(:unvoiced, :dental, :fricative)
|
97
95
|
end
|
98
96
|
}
|
99
97
|
|
@@ -106,7 +104,5 @@ module Spanish
|
|
106
104
|
orth.rules = RULES
|
107
105
|
orth
|
108
106
|
end
|
109
|
-
|
110
107
|
end
|
111
|
-
|
112
|
-
end
|
108
|
+
end
|
data/lib/spanish/phonology.rb
CHANGED
@@ -5,9 +5,10 @@ module Spanish
|
|
5
5
|
|
6
6
|
extend self
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :general_rules
|
9
|
+
attr_reader :optional_rules
|
9
10
|
|
10
|
-
@
|
11
|
+
@general_rules = {
|
11
12
|
:sprinantization => ::Phonology::Rule.new {
|
12
13
|
if voiced? and plosive? and !initial? and precedes(:vocoid) and !follows(:nasal)
|
13
14
|
if non_coronal?
|
@@ -17,11 +18,42 @@ module Spanish
|
|
17
18
|
end
|
18
19
|
end
|
19
20
|
},
|
21
|
+
:nasal_assimilation => ::Phonology::Rule.new {
|
22
|
+
add :bilabial if nasal? and precedes :bilabial
|
23
|
+
}
|
24
|
+
}
|
25
|
+
@optional_rules = {
|
20
26
|
:seseo => ::Phonology::Rule.new {
|
21
27
|
add :alveolar if dental? and unvoiced?
|
22
28
|
},
|
23
29
|
:voicing => ::Phonology::Rule.new {
|
24
30
|
voice if alveolar? and fricative? and precedes(:voiced, :non_vocoid)
|
31
|
+
},
|
32
|
+
:aspiration => ::Phonology::Rule.new {
|
33
|
+
if syllable_final? and alveolar? and fricative? and follows(:vocoid)
|
34
|
+
devoice
|
35
|
+
add :glottal
|
36
|
+
add! :approximant
|
37
|
+
end
|
38
|
+
},
|
39
|
+
:yeismo => ::Phonology::Rule.new {
|
40
|
+
if palatal? and (lateral_approximant? or fricative?)
|
41
|
+
add :palatal, :fricative
|
42
|
+
end
|
43
|
+
},
|
44
|
+
:lleismo => ::Phonology::Rule.new {
|
45
|
+
add :lateral_approximant if (palatal? and fricative?)
|
46
|
+
},
|
47
|
+
:zheismo => ::Phonology::Rule.new {
|
48
|
+
if palatal? and (lateral_approximant? or fricative?)
|
49
|
+
add :postalveolar, :fricative
|
50
|
+
end
|
51
|
+
},
|
52
|
+
:sheismo => ::Phonology::Rule.new {
|
53
|
+
if palatal? and (lateral_approximant? or fricative?)
|
54
|
+
add :postalveolar, :fricative
|
55
|
+
delete :voiced
|
56
|
+
end
|
25
57
|
}
|
26
58
|
}
|
27
59
|
|
data/lib/spanish/syllable.rb
CHANGED
@@ -1,37 +1,5 @@
|
|
1
|
-
# @TODO
|
2
|
-
# Problem words: insubstancialidad, descouella
|
3
1
|
module Spanish
|
4
|
-
class Syllable
|
5
|
-
|
6
|
-
attr_accessor :onset, :coda, :nucleus, :stress
|
7
|
-
|
8
|
-
def initialize(sound = nil)
|
9
|
-
@onset = []
|
10
|
-
@nucleus = []
|
11
|
-
@coda = []
|
12
|
-
add sound if sound
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_a
|
16
|
-
[onset, rime].flatten
|
17
|
-
end
|
18
|
-
|
19
|
-
def valid?
|
20
|
-
!nucleus.empty?
|
21
|
-
end
|
22
|
-
|
23
|
-
def rime
|
24
|
-
[nucleus, coda]
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_s
|
28
|
-
string = to_a.map(&:symbol).join
|
29
|
-
(stress ? "\u02c8" : "") + string
|
30
|
-
end
|
31
|
-
|
32
|
-
def wants?(sound)
|
33
|
-
onset_wants?(sound) or nucleus_wants?(sound) or coda_wants?(sound)
|
34
|
-
end
|
2
|
+
class Syllable < ::Phonology::Syllable
|
35
3
|
|
36
4
|
def onset_wants?(sound)
|
37
5
|
if !nucleus.empty? || sound.vocalic?
|
@@ -58,6 +26,8 @@ module Spanish
|
|
58
26
|
def coda_wants?(sound)
|
59
27
|
if nucleus.empty?
|
60
28
|
false
|
29
|
+
elsif !coda.empty?
|
30
|
+
false if coda.last.approximant?
|
61
31
|
else
|
62
32
|
# Codas don't want a rising dipthong but will accept one at the end of words.
|
63
33
|
sound.consonantal? && !(sound.approximant? && sound.palatal?)
|
@@ -66,19 +36,15 @@ module Spanish
|
|
66
36
|
|
67
37
|
def <<(sound)
|
68
38
|
@stress = true if sound.hints.include?(:primary_stress)
|
69
|
-
|
70
|
-
@onset << sound
|
71
|
-
elsif nucleus_wants?(sound)
|
72
|
-
@nucleus << sound
|
73
|
-
else
|
74
|
-
@coda << sound
|
75
|
-
end
|
39
|
+
super
|
76
40
|
end
|
77
|
-
|
41
|
+
end
|
78
42
|
|
79
|
-
|
80
|
-
|
81
|
-
|
43
|
+
class Syllabifier
|
44
|
+
|
45
|
+
include Enumerable
|
46
|
+
|
47
|
+
attr :index, :sounds, :syllable
|
82
48
|
|
83
49
|
def self.apply_stress(syllables)
|
84
50
|
if syllables.detect {|s| s.stress}
|
@@ -98,78 +64,71 @@ module Spanish
|
|
98
64
|
|
99
65
|
def self.syllabify(arg)
|
100
66
|
arg = arg.kind_of?(String) ? Spanish.get_sounds(arg) : arg
|
101
|
-
apply_stress(
|
67
|
+
apply_stress(new(arg).entries)
|
102
68
|
end
|
103
69
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
attr :index, :sounds, :syllable
|
109
|
-
|
110
|
-
def initialize(sounds)
|
111
|
-
@sounds = sounds
|
112
|
-
end
|
113
|
-
|
114
|
-
def each(&block)
|
115
|
-
begin
|
116
|
-
sounds.each_index { |i| @index = i; append or do_yield(&block) }
|
117
|
-
do_yield(&block)
|
118
|
-
ensure
|
119
|
-
@index = 0
|
120
|
-
@syllable = nil
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
private
|
70
|
+
def initialize(sounds)
|
71
|
+
@sounds = sounds
|
72
|
+
end
|
125
73
|
|
126
|
-
|
127
|
-
|
128
|
-
@
|
74
|
+
def each(&block)
|
75
|
+
begin
|
76
|
+
sounds.each_index { |i| @index = i; append or do_yield(&block) }
|
77
|
+
do_yield(&block)
|
78
|
+
ensure
|
79
|
+
@index = 0
|
80
|
+
@syllable = nil
|
129
81
|
end
|
82
|
+
end
|
130
83
|
|
131
|
-
|
132
|
-
@syllable ||= Syllable.new
|
133
|
-
end
|
84
|
+
private
|
134
85
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
false
|
143
|
-
# Spanish has a strong aversion to syllable-initial consonant clusters
|
144
|
-
# beginning with "s".
|
145
|
-
elsif syllable.wants?(curr) && nex && curr.alveolar? && curr.fricative? && nex.non_vocoid?
|
146
|
-
syllable << curr
|
147
|
-
# Otherwise, the preference is to be the onset of a new syllable. This
|
148
|
-
# is only possible when the syllable we would create has sonority rising
|
149
|
-
# towards the nucleus.
|
150
|
-
elsif nex && syllable.valid? && (nex.sonority - curr.sonority > 1)
|
151
|
-
false
|
152
|
-
# Default action is to append to current syllable.
|
153
|
-
else
|
154
|
-
syllable << curr
|
155
|
-
end
|
156
|
-
end
|
86
|
+
def do_yield(&block)
|
87
|
+
yield syllable
|
88
|
+
# For final syllables, this will only create a new syllable for one-vowel final syllables,
|
89
|
+
# Honestly, I'm a little lost as to why that happens, probably need a code improvement
|
90
|
+
# in some other place.
|
91
|
+
@syllable = Syllable.new(curr) unless !nex && curr.syllable
|
92
|
+
end
|
157
93
|
|
158
|
-
|
159
|
-
|
160
|
-
|
94
|
+
def syllable
|
95
|
+
@syllable ||= Syllable.new
|
96
|
+
end
|
161
97
|
|
162
|
-
|
163
|
-
|
98
|
+
def append
|
99
|
+
return if !curr
|
100
|
+
# Final consonantal has nowhere else to go.
|
101
|
+
if !nex && curr.consonantal?
|
102
|
+
syllable << curr
|
103
|
+
# If there's no room in the syllable, we're forced to start a new one.
|
104
|
+
elsif !syllable.wants? curr
|
105
|
+
false
|
106
|
+
# Spanish has a strong aversion to syllable-initial consonant clusters
|
107
|
+
# beginning with "s".
|
108
|
+
elsif syllable.wants?(curr) && nex && curr.alveolar? && curr.fricative? && nex.non_vocoid?
|
109
|
+
syllable << curr
|
110
|
+
# Otherwise, the preference is to be the onset of a new syllable. This
|
111
|
+
# is only possible when the syllable we would create has sonority rising
|
112
|
+
# towards the nucleus.
|
113
|
+
elsif nex && syllable.valid? && (nex.sonority - curr.sonority > 1)
|
114
|
+
false
|
115
|
+
# Default action is to append to current syllable.
|
116
|
+
else
|
117
|
+
syllable << curr
|
164
118
|
end
|
119
|
+
end
|
165
120
|
|
166
|
-
|
167
|
-
|
168
|
-
|
121
|
+
def curr
|
122
|
+
@sounds[index]
|
123
|
+
end
|
169
124
|
|
125
|
+
def prev
|
126
|
+
@sounds[index - 1] unless index == 0
|
170
127
|
end
|
171
128
|
|
129
|
+
def nex
|
130
|
+
@sounds[index + 1]
|
131
|
+
end
|
172
132
|
|
173
133
|
end
|
174
|
-
|
175
134
|
end
|
data/lib/spanish/version.rb
CHANGED
data/test/phonology_test.rb
CHANGED
@@ -5,7 +5,7 @@ class PhonologyTest < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
test "c as s or k" do
|
7
7
|
assert_sound "kasa", "casa"
|
8
|
-
assert_sound "
|
8
|
+
assert_sound "biθi", "bici"
|
9
9
|
assert_sound "fɾanko", "franco"
|
10
10
|
end
|
11
11
|
|
@@ -30,13 +30,13 @@ class PhonologyTest < Test::Unit::TestCase
|
|
30
30
|
test "y" do
|
31
31
|
assert_sound "i", "y"
|
32
32
|
assert_sound "kaʝo", "cayó"
|
33
|
-
assert_sound "
|
33
|
+
assert_sound "iɣwaθu", "yguazú"
|
34
34
|
assert_sound "doj", "doy"
|
35
35
|
end
|
36
36
|
|
37
37
|
test "voicing" do
|
38
|
-
assert_sound "
|
39
|
-
assert_sound "
|
38
|
+
assert_sound "rasɣo", "rasgo"
|
39
|
+
assert_sound "xaθmin", "jazmín"
|
40
40
|
end
|
41
41
|
|
42
42
|
test "trilled r and flap r" do
|
@@ -3,41 +3,40 @@ require File.expand_path("../test_helper", __FILE__)
|
|
3
3
|
|
4
4
|
class SyllabificationTest < Test::Unit::TestCase
|
5
5
|
|
6
|
-
include Phonology
|
7
6
|
include Spanish
|
8
7
|
|
9
8
|
test "should add consonant to empty onset" do
|
10
9
|
s = Syllable.new
|
11
|
-
assert s.onset_wants? Sound.new("t")
|
12
|
-
assert s.onset_wants? Sound.new("l")
|
13
|
-
assert s.onset_wants? Sound.new("w")
|
10
|
+
assert s.onset_wants? Phonology::Sound.new("t")
|
11
|
+
assert s.onset_wants? Phonology::Sound.new("l")
|
12
|
+
assert s.onset_wants? Phonology::Sound.new("w")
|
14
13
|
end
|
15
14
|
|
16
15
|
test "should not add vowel to empty onsent" do
|
17
16
|
s = Syllable.new
|
18
|
-
assert !s.onset_wants?(Sound.new("o"))
|
17
|
+
assert !s.onset_wants?(Phonology::Sound.new("o"))
|
19
18
|
end
|
20
19
|
|
21
20
|
test "can append consonant to onset if liquid" do
|
22
21
|
s = Syllable.new
|
23
|
-
s.onset << Sound.new("t")
|
24
|
-
assert s.onset_wants? Sound.new("l")
|
25
|
-
assert s.onset_wants? Sound.new("ɾ")
|
22
|
+
s.onset << Phonology::Sound.new("t")
|
23
|
+
assert s.onset_wants? Phonology::Sound.new("l")
|
24
|
+
assert s.onset_wants? Phonology::Sound.new("ɾ")
|
26
25
|
end
|
27
26
|
|
28
27
|
test "can append consonant to onset if approximant" do
|
29
28
|
s = Syllable.new
|
30
|
-
s.onset << Sound.new("k")
|
31
|
-
assert s.onset_wants? Sound.new("w")
|
29
|
+
s.onset << Phonology::Sound.new("k")
|
30
|
+
assert s.onset_wants? Phonology::Sound.new("w")
|
32
31
|
end
|
33
32
|
|
34
33
|
test "can not append sound to onset if non-liquid and non-approximant" do
|
35
34
|
s = Syllable.new
|
36
|
-
s.onset << Sound.new("k")
|
37
|
-
assert !s.onset_wants?(Sound.new("n"))
|
38
|
-
assert !s.onset_wants?(Sound.new("r"))
|
39
|
-
assert !s.onset_wants?(Sound.new("s"))
|
40
|
-
assert !s.onset_wants?(Sound.new("a"))
|
35
|
+
s.onset << Phonology::Sound.new("k")
|
36
|
+
assert !s.onset_wants?(Phonology::Sound.new("n"))
|
37
|
+
assert !s.onset_wants?(Phonology::Sound.new("r"))
|
38
|
+
assert !s.onset_wants?(Phonology::Sound.new("s"))
|
39
|
+
assert !s.onset_wants?(Phonology::Sound.new("a"))
|
41
40
|
end
|
42
41
|
|
43
42
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Norman Clarke
|
@@ -14,13 +14,14 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-07-09 00:00:00 -03:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: phonology
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
24
25
|
requirements:
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
@@ -61,6 +62,7 @@ rdoc_options: []
|
|
61
62
|
require_paths:
|
62
63
|
- lib
|
63
64
|
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
64
66
|
requirements:
|
65
67
|
- - ">="
|
66
68
|
- !ruby/object:Gem::Version
|
@@ -69,6 +71,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
71
|
- 9
|
70
72
|
version: "1.9"
|
71
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
72
75
|
requirements:
|
73
76
|
- - ">="
|
74
77
|
- !ruby/object:Gem::Version
|
@@ -78,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
81
|
requirements: []
|
79
82
|
|
80
83
|
rubyforge_project: "[none]"
|
81
|
-
rubygems_version: 1.3.
|
84
|
+
rubygems_version: 1.3.7
|
82
85
|
signing_key:
|
83
86
|
specification_version: 3
|
84
87
|
summary: Spanish phonology, orthography and grammar library for Ruby.
|