math_store.js revision cedac228d2dd51db4b79ea1e72c7f249408ee061
180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru// Copyright 2014 The Chromium Authors. All rights reserved. 280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru// Use of this source code is governed by a BSD-style license that can be 380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru// found in the LICENSE file. 480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/** 680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * @fileoverview Rule store for math syntax tree nodes. 780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */ 880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.provide('cvox.MathStore'); 1080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 1180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.require('cvox.AbstractTts'); 1280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.require('cvox.BaseRuleStore'); 1380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.require('cvox.ChromeVox'); 1480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.require('cvox.NavMathDescription'); 1580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.require('cvox.SpeechRule'); 1680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.require('cvox.TraverseMath'); 1780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 1880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 1980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/** 20363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * A store for Math rules. 2180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * @constructor 2280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * @extends {cvox.BaseRuleStore} 2380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */ 2480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querucvox.MathStore = function() { 2580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru goog.base(this); 2680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 27363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger /** 2880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * @override 29363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger */ 3080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru this.dynamicCstrAttribs = [ 31363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger cvox.SpeechRule.DynamicCstrAttrib.DOMAIN, 3280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru cvox.SpeechRule.DynamicCstrAttrib.STYLE 33363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger ]; 3480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 35363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger /** 3680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * @override 3780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */ 3880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru this.defaultTtsProps = [cvox.AbstractTts.PITCH, cvox.AbstractTts.RATE]; 3980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}; 4080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querugoog.inherits(cvox.MathStore, cvox.BaseRuleStore); 41363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 42d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger/** This adds domain to dynamic constraint annotation. */ 4380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querucvox.SpeechRule.DynamicCstrAttrib.DOMAIN = 'domain'; 44363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 45d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger 4680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/** 4780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * @override 4880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */ 4980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querucvox.MathStore.prototype.defineRule = function( 5080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru name, dynamic, action, query, cstr) { 51 var dynamicCstr = this.parseDynamicConstraint(dynamic); 52 var cstrList = Array.prototype.slice.call(arguments, 4); 53 // We can not use goog.base due to variable number of constraint arguments. 54 var rule = cvox.MathStore.superClass_.defineRule.apply( 55 this, [name, dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.STYLE], 56 action, query].concat(cstrList)); 57 // In the superclass the dynamic constraint only contains style annotations. 58 // We now set the proper dynamic constraint that contains in addition a 59 // a domain attribute/value pair. 60 rule.dynamicCstr = dynamicCstr; 61 this.removeDuplicates(rule); 62 return rule; 63}; 64 65 66/** 67 * Parses the dynamic constraint for math rules, consisting of a domain and 68 * style information, given as 'domain.style'. 69 * @param {string} cstr A string representation of the dynamic constraint. 70 * @return {!cvox.SpeechRule.DynamicCstr} The dynamic constraint. 71 */ 72cvox.MathStore.prototype.parseDynamicConstraint = function(cstr) { 73 var domainStyle = cstr.split('.'); 74 if (!domainStyle[0] || !domainStyle[1]) { 75 throw new cvox.SpeechRule.OutputError('Invalid domain assignment:' + cstr); 76 } 77 return cvox.MathStore.createDynamicConstraint(domainStyle[0], domainStyle[1]); 78}; 79 80 81/** 82 * Creates a dynamic constraint annotation for math rules from domain and style 83 * values. 84 * @param {string} domain Domain annotation. 85 * @param {string} style Style annotation. 86 * @return {!cvox.SpeechRule.DynamicCstr} 87 */ 88cvox.MathStore.createDynamicConstraint = function(domain, style) { 89 var dynamicCstr = {}; 90 dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.DOMAIN] = domain; 91 dynamicCstr[cvox.SpeechRule.DynamicCstrAttrib.STYLE] = style; 92 return dynamicCstr; 93}; 94 95 96/** 97 * Adds an alias for an existing rule. 98 * @param {string} name The name of the rule. 99 * @param {string} dynamic A math domain and style assignment. 100 * @param {string} query Precondition query of the rule. 101 * @param {...string} cstr Additional static precondition constraints. 102 */ 103cvox.MathStore.prototype.defineUniqueRuleAlias = function( 104 name, dynamic, query, cstr) { 105 var dynamicCstr = this.parseDynamicConstraint(dynamic); 106 var rule = this.findRule( 107 goog.bind( 108 function(rule) { 109 return rule.name == name && 110 this.testDynamicConstraints(dynamicCstr, rule);}, 111 this)); 112 if (!rule) { 113 throw new cvox.SpeechRule.OutputError( 114 'Rule named ' + name + ' with style ' + dynamic + ' does not exist.'); 115 } 116 this.addAlias_(rule, query, Array.prototype.slice.call(arguments, 3)); 117}; 118 119 120/** 121 * Adds an alias for an existing rule. 122 * @param {string} name The name of the rule. 123 * @param {string} query Precondition query of the rule. 124 * @param {...string} cstr Additional static precondition constraints. 125 */ 126cvox.MathStore.prototype.defineRuleAlias = function(name, query, cstr) { 127 var rule = this.findRule(function(rule) { 128 return rule.name == name;}); 129 if (!rule) { 130 throw new cvox.SpeechRule.OutputError( 131 'Rule with named ' + name + ' does not exist.'); 132 } 133 this.addAlias_(rule, query, Array.prototype.slice.call(arguments, 2)); 134}; 135 136 137/** 138 * Adds an alias for an existing rule. 139 * @param {string} name The name of the rule. 140 * @param {string} query Precondition query of the rule. 141 * @param {...string} cstr Additional static precondition constraints. 142 */ 143cvox.MathStore.prototype.defineRulesAlias = function(name, query, cstr) { 144 var rules = this.findAllRules(function(rule) {return rule.name == name;}); 145 if (rules.length == 0) { 146 throw new cvox.SpeechRule.OutputError( 147 'Rule with name ' + name + ' does not exist.'); 148 } 149 var cstrList = Array.prototype.slice.call(arguments, 2); 150 rules.forEach(goog.bind( 151 function(rule) { 152 this.addAlias_(rule, query, cstrList); 153 }, 154 this)); 155}; 156 157 158/** 159 * Adds a new speech rule as alias of the given rule. 160 * @param {cvox.SpeechRule} rule The existing rule. 161 * @param {string} query Precondition query of the rule. 162 * @param {Array.<string>} cstrList List of additional constraints. 163 * @private 164 */ 165cvox.MathStore.prototype.addAlias_ = function(rule, query, cstrList) { 166 var prec = new cvox.SpeechRule.Precondition(query, cstrList); 167 var newRule = new cvox.SpeechRule( 168 rule.name, rule.dynamicCstr, prec, rule.action); 169 newRule.name = rule.name; 170 this.addRule(newRule); 171}; 172 173 174// Evaluator 175/** 176 * @override 177 */ 178cvox.MathStore.prototype.evaluateDefault = function(node) { 179 return this.evaluateString_(node.textContent); 180}; 181 182 183/** 184 * Evaluates a single string of a math expressions. The method splits the given 185 * string into components such as single characters, function names or words, 186 * numbers, etc. and creates the appropriate navigation descriptions. 187 * @param {string} str A string. 188 * @return {!Array.<cvox.NavDescription>} Messages for the math expression. 189 * @private 190 */ 191cvox.MathStore.prototype.evaluateString_ = function(str) { 192 var descs = new Array(); 193 if (str.match(/^\s+$/)) { 194 // Nothing but whitespace: Ignore. 195 return descs; 196 } 197 var split = cvox.MathStore.removeEmpty_(str.replace(/\s/g, ' ').split(' ')); 198 for (var i = 0, s; s = split[i]; i++) { 199 if (s.length == 1) { 200 descs.push(this.evaluate_(s)); 201 } else if (s.match(/^[a-zA-Z]+$/)) { 202 descs.push(this.evaluate_(s)); 203 } else { 204 // Break up string even further wrt. symbols vs alphanum substrings. 205 var rest = s; 206 var count = 0; 207 while (rest) { 208 var num = rest.match(/^\d+/); 209 var alpha = rest.match(/^[a-zA-Z]+/); 210 if (num) { 211 descs.push(this.evaluate_(num[0])); 212 rest = rest.substring(num[0].length); 213 } else if (alpha) { 214 descs.push(this.evaluate_(alpha[0])); 215 rest = rest.substring(alpha[0].length); 216 } else { 217 // Dealing with surrogate pairs. 218 var chr = rest[0]; 219 var code = chr.charCodeAt(0); 220 if (0xD800 <= code && code <= 0xDBFF && 221 rest.length > 1 && !isNaN(rest.charCodeAt(1))) { 222 descs.push(this.evaluate_(rest.slice(0, 2))); 223 rest = rest.substring(2); 224 } else { 225 descs.push(this.evaluate_(rest[0])); 226 rest = rest.substring(1); 227 } 228 } 229 } 230 } 231 } 232 return descs; 233}; 234 235 236/** 237 * Creates a new Navigation Description for a math expression that be used by 238 * the background tts. 239 * @param {string} text to be translated. 240 * @return {cvox.NavDescription} Navigation description for the 241 * math expression. 242 * @private 243 */ 244cvox.MathStore.prototype.evaluate_ = function(text) { 245 if (cvox.ChromeVox.host['mathMap']) { 246 // VS: Change this for android! 247 return cvox.ChromeVox.host['mathMap'].evaluate( 248 text, 249 cvox.TraverseMath.getInstance().domain, 250 cvox.TraverseMath.getInstance().style); 251 } 252 return new cvox.NavMathDescription( 253 {'text': text, 254 'domain': cvox.TraverseMath.getInstance().domain, 255 'style': cvox.TraverseMath.getInstance().style 256 }); 257}; 258 259 260/** 261 * Removes all empty strings from an array of strings. 262 * @param {Array.<string>} strs An array of strings. 263 * @return {Array.<string>} The cleaned array. 264 * @private 265 */ 266cvox.MathStore.removeEmpty_ = function(strs) { 267 return strs.filter(function(str) {return str;}); 268}; 269