spanish 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- Phonology.rules.values.each do |rule|
25
- sequence.apply_rule!(rule)
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
@@ -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" then get(:unvoiced, :dental, :fricative)
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
@@ -5,9 +5,10 @@ module Spanish
5
5
 
6
6
  extend self
7
7
 
8
- attr_reader :rules
8
+ attr_reader :general_rules
9
+ attr_reader :optional_rules
9
10
 
10
- @rules = {
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
 
@@ -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
- if onset_wants?(sound)
70
- @onset << sound
71
- elsif nucleus_wants?(sound)
72
- @nucleus << sound
73
- else
74
- @coda << sound
75
- end
39
+ super
76
40
  end
77
- alias add <<
41
+ end
78
42
 
79
- def empty?
80
- onset.empty? && nucleus.empty? && coda.empty?
81
- end
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(Syllables.new(arg).entries)
67
+ apply_stress(new(arg).entries)
102
68
  end
103
69
 
104
- class Syllables
105
-
106
- include Enumerable
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
- def do_yield(&block)
127
- yield syllable
128
- @syllable = Syllable.new(curr)
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
- def syllable
132
- @syllable ||= Syllable.new
133
- end
84
+ private
134
85
 
135
- def append
136
- return if !curr
137
- # Final consonantal has nowhere else to go.
138
- if !nex && curr.consonantal?
139
- syllable << curr
140
- # If there's no room in the syllable, we're forced to start a new one.
141
- elsif !syllable.wants? curr
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
- def curr
159
- @sounds[index]
160
- end
94
+ def syllable
95
+ @syllable ||= Syllable.new
96
+ end
161
97
 
162
- def prev
163
- @sounds[index - 1] unless index == 0
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
- def nex
167
- @sounds[index + 1]
168
- end
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
@@ -1,3 +1,3 @@
1
1
  module Spanish
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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 "bisi", "bici"
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 "iɣwasu", "yguazú"
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 "razɣo", "rasgo"
39
- assert_sound "xazmin", "jazmín"
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
@@ -1,3 +1,6 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup
1
4
  require "test/unit"
2
5
  require File.expand_path("../../lib/spanish", __FILE__)
3
6
  include Spanish
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
9
- version: 0.0.2
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-05-20 00:00:00 -03:00
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.6
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.