lib/ruby_speech/grxml/matcher.rb
require 'ruby_speech/grxml/match'
require 'ruby_speech/grxml/no_match'
require 'ruby_speech/grxml/potential_match'
require 'ruby_speech/grxml/max_match'
require 'ruby_speech/ruby_speech'
if RUBY_PLATFORM =~ /java/
require 'jruby'
com.adhearsion.ruby_speech.RubySpeechService.new.basicLoad(JRuby.runtime)
end
module RubySpeech
module GRXML
class Matcher
UTTERANCE_CONVERTER = Hash.new { |hash, key| hash[key] = key }
UTTERANCE_CONVERTER['*'] = 'star'
UTTERANCE_CONVERTER['#'] = 'pound'
attr_reader :grammar
def initialize(grammar)
@grammar = grammar
prepare_grammar
compile_regex regexp_content.gsub(/\?<[\w\d\s]*>/, '')
end
##
# Checks the grammar for a match against an input string
#
# @param [String] other the input string to check for a match with the grammar
#
# @return [NoMatch, PotentialMatch, Match, MaxMatch] depending on the result of a match attempt. A potential match indicates that the buffer is valid, but incomplete. A MaxMatch is differentiated from a Match in that it cannot accept further input. If a match can be found, it will be returned with appropriate mode/confidence/utterance and interpretation attributes
#
# @example A grammar that takes a 4 digit pin terminated by hash, or the *9 escape sequence
# ```ruby
# grammar = RubySpeech::GRXML.draw :mode => :dtmf, :root => 'pin' do
# rule :id => 'digit' do
# one_of do
# ('0'..'9').map { |d| item { d } }
# end
# end
#
# rule :id => 'pin', :scope => 'public' do
# one_of do
# item do
# item :repeat => '4' do
# ruleref :uri => '#digit'
# end
# "#"
# end
# item do
# "\* 9"
# end
# end
# end
# end
#
# matcher = RubySpeech::GRXML::Matcher.new grammar
#
# >> matcher.match '*9'
# => #<RubySpeech::GRXML::Match:0x00000100ae5d98
# @mode = :dtmf,
# @confidence = 1,
# @utterance = "*9",
# @interpretation = "*9"
# >
# >> matcher.match '1234#'
# => #<RubySpeech::GRXML::Match:0x00000100b7e020
# @mode = :dtmf,
# @confidence = 1,
# @utterance = "1234#",
# @interpretation = "1234#"
# >
# >> matcher.match '5678#'
# => #<RubySpeech::GRXML::Match:0x00000101218688
# @mode = :dtmf,
# @confidence = 1,
# @utterance = "5678#",
# @interpretation = "5678#"
# >
# >> matcher.match '1111#'
# => #<RubySpeech::GRXML::Match:0x000001012f69d8
# @mode = :dtmf,
# @confidence = 1,
# @utterance = "1111#",
# @interpretation = "1111#"
# >
# >> matcher.match '111'
# => #<RubySpeech::GRXML::NoMatch:0x00000101371660>
# ```
#
def match(buffer)
find_match buffer
end
private
def regexp_content
'^' + grammar.root_rule.children.map(&:regexp_content).join + '$'
end
def prepare_grammar
grammar.inline!
grammar.tokenize!
grammar.normalize_whitespace
end
def match_for_buffer(buffer, maximal = false)
match_class = maximal ? MaxMatch : Match
match_class.new mode: grammar.mode,
confidence: grammar.dtmf? ? 1 : 0,
utterance: buffer,
interpretation: interpret_utterance(buffer)
end
def interpret_utterance(utterance)
find_tag(utterance) || utterance.chars.inject([]) do |array, digit|
array << "dtmf-#{UTTERANCE_CONVERTER[digit]}"
end.join(' ')
end
def find_tag(utterance)
match = /#{regexp_content}/.match(utterance)
return if match.captures.all?(&:nil?)
last_capture_index = match.captures.size - 1 - match.captures.reverse.find_index { |item| !item.nil? }
group = match.names[last_capture_index]
group && group[1..-1]
end
end
end
end