Home | History | Annotate | Download | only in speech_rules
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * @fileoverview Rule store for math syntax tree nodes.
      7  */
      8 
      9 goog.provide('cvox.MathStore');
     10 
     11 goog.require('cvox.AbstractTts');
     12 goog.require('cvox.BaseRuleStore');
     13 goog.require('cvox.ChromeVox');
     14 goog.require('cvox.NavMathDescription');
     15 goog.require('cvox.SpeechRule');
     16 goog.require('cvox.TraverseMath');
     17 
     18 
     19 /**
     20  * A store for Math rules.
     21  * @constructor
     22  * @extends {cvox.BaseRuleStore}
     23  */
     24 cvox.MathStore = function() {
     25   goog.base(this);
     26 
     27   /**
     28    * @override
     29    */
     30   this.dynamicCstrAttribs = [
     31     cvox.SpeechRule.DynamicCstrAttrib.DOMAIN,
     32     cvox.SpeechRule.DynamicCstrAttrib.STYLE
     33     ];
     34 
     35   /**
     36    * @override
     37    */
     38   this.defaultTtsProps = [cvox.AbstractTts.PITCH, cvox.AbstractTts.RATE];
     39 };
     40 goog.inherits(cvox.MathStore, cvox.BaseRuleStore);
     41 
     42 /** This adds domain to dynamic constraint annotation. */
     43 cvox.SpeechRule.DynamicCstrAttrib.DOMAIN = 'domain';
     44 
     45 
     46 /**
     47  * @override
     48  */
     49 cvox.MathStore.prototype.defineRule = function(
     50     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  */
     72 cvox.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  */
     88 cvox.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  */
    103 cvox.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  */
    126 cvox.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  */
    143 cvox.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  */
    165 cvox.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  */
    178 cvox.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  */
    191 cvox.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  */
    244 cvox.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  */
    266 cvox.MathStore.removeEmpty_ = function(strs) {
    267   return strs.filter(function(str) {return str;});
    268 };
    269