spanish 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|