ruby_speech 1.1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,6 +14,7 @@ module RubySpeech
14
14
  end
15
15
 
16
16
  autoload :Match
17
+ autoload :Matcher
17
18
  autoload :NoMatch
18
19
  autoload :PotentialMatch
19
20
 
@@ -24,23 +24,6 @@ module RubySpeech
24
24
  def regexp_content # :nodoc:
25
25
  children.map(&:regexp_content).join
26
26
  end
27
-
28
- def potential_match?(other)
29
- false
30
- end
31
-
32
- def max_input_length
33
- 0
34
- end
35
-
36
- def longest_potential_match(input)
37
- input.dup.tap do |longest_input|
38
- begin
39
- return longest_input if potential_match? longest_input
40
- longest_input.chop!
41
- end until longest_input.length.zero?
42
- end
43
- end
44
27
  end # Element
45
28
  end # GRXML
46
29
  end # RubySpeech
@@ -149,99 +149,6 @@ module RubySpeech
149
149
  end
150
150
  end
151
151
 
152
- ##
153
- # Checks the grammar for a match against an input string
154
- #
155
- # @param [String] other the input string to check for a match with the grammar
156
- #
157
- # @return [NoMatch, Match] depending on the result of a match attempt. If a match can be found, it will be returned with appropriate mode/confidence/utterance and interpretation attributes
158
- #
159
- # @example A grammar that takes a 4 digit pin terminated by hash, or the *9 escape sequence
160
- # ```ruby
161
- # grammar = RubySpeech::GRXML.draw :mode => :dtmf, :root => 'pin' do
162
- # rule :id => 'digit' do
163
- # one_of do
164
- # ('0'..'9').map { |d| item { d } }
165
- # end
166
- # end
167
- #
168
- # rule :id => 'pin', :scope => 'public' do
169
- # one_of do
170
- # item do
171
- # item :repeat => '4' do
172
- # ruleref :uri => '#digit'
173
- # end
174
- # "#"
175
- # end
176
- # item do
177
- # "\* 9"
178
- # end
179
- # end
180
- # end
181
- # end
182
- #
183
- # >> subject.match '*9'
184
- # => #<RubySpeech::GRXML::Match:0x00000100ae5d98
185
- # @mode = :dtmf,
186
- # @confidence = 1,
187
- # @utterance = "*9",
188
- # @interpretation = "*9"
189
- # >
190
- # >> subject.match '1234#'
191
- # => #<RubySpeech::GRXML::Match:0x00000100b7e020
192
- # @mode = :dtmf,
193
- # @confidence = 1,
194
- # @utterance = "1234#",
195
- # @interpretation = "1234#"
196
- # >
197
- # >> subject.match '111'
198
- # => #<RubySpeech::GRXML::PotentialMatch:0x00000101371660>
199
- #
200
- # >> subject.match '11111'
201
- # => #<RubySpeech::GRXML::NoMatch:0x00000101371936>
202
- #
203
- # ```
204
- #
205
- def match(other)
206
- other = other.dup
207
- regex = to_regexp
208
- return check_for_potential_match(other) if regex == //
209
- match = regex.match other
210
- return check_for_potential_match(other) unless match
211
-
212
- Match.new :mode => mode,
213
- :confidence => dtmf? ? 1 : 0,
214
- :utterance => other,
215
- :interpretation => interpret_utterance(other)
216
- end
217
-
218
- def check_for_potential_match(other)
219
- potential_match?(other) ? PotentialMatch.new : NoMatch.new
220
- end
221
-
222
- def potential_match?(other)
223
- root_rule.children.each do |token|
224
- return true if other.length.zero?
225
- longest_potential_match = token.longest_potential_match other
226
- return false if longest_potential_match.length.zero?
227
- other.gsub! /^#{Regexp.escape longest_potential_match}/, ''
228
- end
229
- other.length.zero?
230
- end
231
-
232
- ##
233
- # Converts the grammar into a regular expression for matching
234
- #
235
- # @return [Regexp] a regular expression which is equivalent to the grammar
236
- #
237
- def to_regexp
238
- /^#{regexp_content.join}$/
239
- end
240
-
241
- def regexp_content
242
- root_rule.children.map &:regexp_content
243
- end
244
-
245
152
  def dtmf?
246
153
  mode == :dtmf
247
154
  end
@@ -270,16 +177,6 @@ module RubySpeech
270
177
  !root || root_rule
271
178
  end
272
179
 
273
- def interpret_utterance(utterance)
274
- conversion = Hash.new { |hash, key| hash[key] = key }
275
- conversion['*'] = 'star'
276
- conversion['#'] = 'pound'
277
-
278
- utterance.chars.inject [] do |array, digit|
279
- array << "dtmf-#{conversion[digit]}"
280
- end.join ' '
281
- end
282
-
283
180
  def split_tokens(element)
284
181
  element.to_s.split(/(\".*\")/).reject(&:empty?).map do |string|
285
182
  match = string.match /^\"(.*)\"$/
@@ -139,27 +139,6 @@ module RubySpeech
139
139
  super
140
140
  end
141
141
  end
142
-
143
- def potential_match?(other)
144
- tokens = children
145
- return false if other.length > max_input_length
146
- other.chars.each_with_index do |digit, index|
147
- index -= tokens.size until index < tokens.size if repeat
148
- return false unless tokens[index].potential_match?(digit)
149
- end
150
- true
151
- end
152
-
153
- def max_input_length # :nodoc:
154
- case repeat
155
- when Range
156
- children.size * repeat.max
157
- when Integer
158
- children.size * repeat
159
- else
160
- children.size
161
- end
162
- end
163
142
  end # Item
164
143
  end # GRXML
165
144
  end # RubySpeech
@@ -0,0 +1,129 @@
1
+ require 'ruby_speech/ruby_speech'
2
+
3
+ if RUBY_PLATFORM =~ /java/
4
+ require 'jruby'
5
+ com.benlangfeld.ruby_speech.RubySpeechService.new.basicLoad(JRuby.runtime)
6
+ end
7
+
8
+ module RubySpeech
9
+ module GRXML
10
+ class Matcher
11
+
12
+ BLANK_REGEX = //.freeze
13
+
14
+ attr_reader :grammar, :regex
15
+
16
+ def initialize(grammar)
17
+ @grammar = grammar
18
+ prepare_grammar
19
+ @regex = /^#{regexp_content.join}$/
20
+ end
21
+
22
+ ##
23
+ # Checks the grammar for a match against an input string
24
+ #
25
+ # @param [String] other the input string to check for a match with the grammar
26
+ #
27
+ # @return [NoMatch, Match] depending on the result of a match attempt. If a match can be found, it will be returned with appropriate mode/confidence/utterance and interpretation attributes
28
+ #
29
+ # @example A grammar that takes a 4 digit pin terminated by hash, or the *9 escape sequence
30
+ # ```ruby
31
+ # grammar = RubySpeech::GRXML.draw :mode => :dtmf, :root => 'pin' do
32
+ # rule :id => 'digit' do
33
+ # one_of do
34
+ # ('0'..'9').map { |d| item { d } }
35
+ # end
36
+ # end
37
+ #
38
+ # rule :id => 'pin', :scope => 'public' do
39
+ # one_of do
40
+ # item do
41
+ # item :repeat => '4' do
42
+ # ruleref :uri => '#digit'
43
+ # end
44
+ # "#"
45
+ # end
46
+ # item do
47
+ # "\* 9"
48
+ # end
49
+ # end
50
+ # end
51
+ # end
52
+ #
53
+ # matcher = RubySpeech::GRXML::Matcher.new grammar
54
+ #
55
+ # >> matcher.match '*9'
56
+ # => #<RubySpeech::GRXML::Match:0x00000100ae5d98
57
+ # @mode = :dtmf,
58
+ # @confidence = 1,
59
+ # @utterance = "*9",
60
+ # @interpretation = "*9"
61
+ # >
62
+ # >> matcher.match '1234#'
63
+ # => #<RubySpeech::GRXML::Match:0x00000100b7e020
64
+ # @mode = :dtmf,
65
+ # @confidence = 1,
66
+ # @utterance = "1234#",
67
+ # @interpretation = "1234#"
68
+ # >
69
+ # >> matcher.match '5678#'
70
+ # => #<RubySpeech::GRXML::Match:0x00000101218688
71
+ # @mode = :dtmf,
72
+ # @confidence = 1,
73
+ # @utterance = "5678#",
74
+ # @interpretation = "5678#"
75
+ # >
76
+ # >> matcher.match '1111#'
77
+ # => #<RubySpeech::GRXML::Match:0x000001012f69d8
78
+ # @mode = :dtmf,
79
+ # @confidence = 1,
80
+ # @utterance = "1111#",
81
+ # @interpretation = "1111#"
82
+ # >
83
+ # >> matcher.match '111'
84
+ # => #<RubySpeech::GRXML::NoMatch:0x00000101371660>
85
+ # ```
86
+ #
87
+ def match(buffer)
88
+ buffer = buffer.dup
89
+
90
+ return check_potential_match(buffer) if regex == BLANK_REGEX
91
+
92
+ check_full_match(buffer) || check_potential_match(buffer) || NoMatch.new
93
+ end
94
+
95
+ private
96
+
97
+ def prepare_grammar
98
+ grammar.inline!
99
+ grammar.tokenize!
100
+ grammar.normalize_whitespace
101
+ end
102
+
103
+ def check_full_match(buffer)
104
+ match = regex.match buffer
105
+
106
+ return unless match
107
+
108
+ Match.new :mode => grammar.mode,
109
+ :confidence => grammar.dtmf? ? 1 : 0,
110
+ :utterance => buffer,
111
+ :interpretation => interpret_utterance(buffer)
112
+ end
113
+
114
+ def regexp_content
115
+ grammar.root_rule.children.map &:regexp_content
116
+ end
117
+
118
+ def interpret_utterance(utterance)
119
+ conversion = Hash.new { |hash, key| hash[key] = key }
120
+ conversion['*'] = 'star'
121
+ conversion['#'] = 'pound'
122
+
123
+ utterance.chars.inject [] do |array, digit|
124
+ array << "dtmf-#{conversion[digit]}"
125
+ end.join ' '
126
+ end
127
+ end
128
+ end
129
+ end
@@ -26,10 +26,6 @@ module RubySpeech
26
26
  def regexp_content # :nodoc:
27
27
  "(#{children.map(&:regexp_content).join '|'})"
28
28
  end
29
-
30
- def potential_match?(input)
31
- children.any? { |c| c.potential_match? input }
32
- end
33
29
  end # OneOf
34
30
  end # GRXML
35
31
  end # RubySpeech
@@ -26,10 +26,6 @@ module RubySpeech
26
26
  def regexp_content # :nodoc:
27
27
  Regexp.escape content
28
28
  end
29
-
30
- def potential_match?(other)
31
- other == content
32
- end
33
29
  end # Token
34
30
  end # GRXML
35
31
  end # RubySpeech
@@ -2,8 +2,7 @@ module RubySpeech
2
2
  module NLSML
3
3
  extend ActiveSupport::Autoload
4
4
 
5
- NLSML_NAMESPACE = 'http://www.w3c.org/2000/11/nlsml'
6
- XFORMS_NAMESPACE = 'http://www.w3.org/2000/xforms'
5
+ NLSML_NAMESPACE = 'http://www.ietf.org/xml/ns/mrcpv2'
7
6
 
8
7
  eager_autoload do
9
8
  autoload :Builder
@@ -4,7 +4,7 @@ module RubySpeech
4
4
  attr_reader :document
5
5
 
6
6
  def initialize(options = {}, &block)
7
- options = {'xmlns' => NLSML_NAMESPACE, 'xmlns:xf' => XFORMS_NAMESPACE}.merge(options)
7
+ options = {'xmlns' => NLSML_NAMESPACE}.merge(options)
8
8
  @document = Nokogiri::XML::Builder.new do |builder|
9
9
  builder.result options do |r|
10
10
  apply_block r, &block
@@ -14,19 +14,11 @@ module RubySpeech
14
14
 
15
15
  def interpretation(*args, &block)
16
16
  if args.last.respond_to?(:has_key?) && args.last.has_key?(:confidence)
17
- args.last[:confidence] = (args.last[:confidence] * 100).to_i
17
+ args.last[:confidence] = args.last[:confidence].to_f
18
18
  end
19
19
  @result.send :interpretation, *args, &block
20
20
  end
21
21
 
22
- def model(*args, &block)
23
- xf_namespaced_element :model, *args, &block
24
- end
25
-
26
- def instance(*args, &block)
27
- xf_namespaced_element :instance, *args, &block
28
- end
29
-
30
22
  def method_missing(method_name, *args, &block)
31
23
  @result.send method_name, *args, &block
32
24
  end
@@ -37,11 +29,6 @@ module RubySpeech
37
29
  @result = result
38
30
  instance_eval &block
39
31
  end
40
-
41
- def xf_namespaced_element(element_name, *args, &block)
42
- namespace = @result.send :[], 'xf'
43
- namespace.send element_name, &block
44
- end
45
32
  end
46
33
  end
47
34
  end
@@ -4,8 +4,11 @@ module RubySpeech
4
4
  module NLSML
5
5
  class Document < SimpleDelegator
6
6
  def initialize(xml)
7
+ unless xml.root.namespace
8
+ xml.root.default_namespace = NLSML_NAMESPACE
9
+ xml = Nokogiri::XML.parse xml.to_xml, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS
10
+ end
7
11
  super
8
- @xml = xml
9
12
  end
10
13
 
11
14
  def grammar
@@ -41,19 +44,19 @@ module RubySpeech
41
44
  end
42
45
 
43
46
  def nomatch_elements
44
- result.xpath 'ns:interpretation/ns:input/ns:nomatch|interpretation/input/nomatch', 'ns' => NLSML_NAMESPACE
47
+ result.xpath 'ns:interpretation/ns:input/ns:nomatch', 'ns' => NLSML_NAMESPACE
45
48
  end
46
49
 
47
50
  def noinput_elements
48
- result.xpath 'ns:interpretation/ns:input/ns:noinput|interpretation/input/noinput', 'ns' => NLSML_NAMESPACE
51
+ result.xpath 'ns:interpretation/ns:input/ns:noinput', 'ns' => NLSML_NAMESPACE
49
52
  end
50
53
 
51
54
  def input_elements
52
- result.xpath 'ns:interpretation/ns:input|interpretation/input', 'ns' => NLSML_NAMESPACE
55
+ result.xpath 'ns:interpretation/ns:input', 'ns' => NLSML_NAMESPACE
53
56
  end
54
57
 
55
58
  def input_hash_for_interpretation(interpretation)
56
- input_element = interpretation.at_xpath '(ns:input|input)', 'ns' => NLSML_NAMESPACE
59
+ input_element = interpretation.at_xpath 'ns:input', 'ns' => NLSML_NAMESPACE
57
60
  { content: input_element.content }.tap do |h|
58
61
  h[:mode] = input_element['mode'].to_sym if input_element['mode']
59
62
  end
@@ -73,10 +76,11 @@ module RubySpeech
73
76
  end
74
77
 
75
78
  def instance_elements(interpretation)
76
- interpretation.xpath '(xf:instance|ns:instance|instance)', 'xf' => XFORMS_NAMESPACE, 'ns' => NLSML_NAMESPACE
79
+ interpretation.xpath 'ns:instance', 'ns' => NLSML_NAMESPACE
77
80
  end
78
81
 
79
82
  def element_children_key_value(element)
83
+ return element.children.first.content if element.children.first.is_a?(Nokogiri::XML::Text)
80
84
  element.children.inject({}) do |acc, child|
81
85
  acc[child.node_name.to_sym] = case child.children.count
82
86
  when 0
@@ -96,7 +100,7 @@ module RubySpeech
96
100
 
97
101
  def interpretation_hash_for_interpretation(interpretation)
98
102
  {
99
- confidence: interpretation['confidence'].to_f/100,
103
+ confidence: interpretation['confidence'].to_f,
100
104
  input: input_hash_for_interpretation(interpretation),
101
105
  instance: instance_hash_for_interpretation(interpretation),
102
106
  instances: instances_collection_for_interpretation(interpretation)
@@ -108,8 +112,8 @@ module RubySpeech
108
112
  end
109
113
 
110
114
  def interpretation_nodes
111
- nodes = result.xpath '(ns:interpretation|interpretation)', 'ns' => NLSML_NAMESPACE
112
- nodes.sort_by { |int| -int[:confidence].to_i }
115
+ nodes = result.xpath 'ns:interpretation', 'ns' => NLSML_NAMESPACE
116
+ nodes.sort_by { |int| -int[:confidence].to_f }
113
117
  end
114
118
  end
115
119
  end