1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file.
4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @fileoverview An interface definition of a speech rule.
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A speech rule is a data structure along with supporting methods that
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * stipulates how to transform a tree structure such as XML, a browser DOM, or
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * HTML into a format (usually strings) suitable for rendering by a
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * text-to-speech engine.
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Speech rules consists of a variable number of speech rule components. Each
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * component describes how to construct a single utterance. Text-to-speech
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * renders the components in order.
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.SpeechRule');
19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.SpeechRule.Action');
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.SpeechRule.Component');
21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.SpeechRule.DynamicCstr');
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.SpeechRule.Precondition');
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.SpeechRule.Type');
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Creates a speech rule with precondition, actions and admin information.
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} name The name of the rule.
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraint annotations
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *     of the rule.
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {cvox.SpeechRule.Precondition} prec Precondition of the rule.
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {cvox.SpeechRule.Action} action Action of the speech rule.
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule = function(name, dynamic, prec, action) {
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {string} */
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.name = name;
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {cvox.SpeechRule.DynamicCstr} */
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.dynamicCstr = dynamic;
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {cvox.SpeechRule.Precondition} */
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.precondition = prec;
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {cvox.SpeechRule.Action} */
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.action = action;
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @override
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.prototype.toString = function() {
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var cstrStrings = [];
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (var key in this.dynamicCstr) {
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    cstrStrings.push(this.dynamicCstr[key]);
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return this.name + ' | ' + cstrStrings.join('.') + ' | ' +
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    this.precondition.toString() + ' ==> ' +
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    this.action.toString();
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Mapping for types of speech rule components.
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @enum {string}
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Type = {
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  NODE: 'NODE',
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  MULTI: 'MULTI',
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  TEXT: 'TEXT',
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  PERSONALITY: 'PERSONALITY'
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Maps a string to a valid speech rule type.
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} str Input string.
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {cvox.SpeechRule.Type}
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Type.fromString = function(str) {
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  switch (str) {
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case '[n]': return cvox.SpeechRule.Type.NODE;
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case '[m]': return cvox.SpeechRule.Type.MULTI;
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case '[t]': return cvox.SpeechRule.Type.TEXT;
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case '[p]': return cvox.SpeechRule.Type.PERSONALITY;
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    default: throw 'Parse error: ' + str;
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Maps a speech rule type to a human-readable string.
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {cvox.SpeechRule.Type} speechType
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} Output string.
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Type.toString = function(speechType) {
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  switch (speechType) {
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.NODE: return '[n]';
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.MULTI: return '[m]';
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.TEXT: return '[t]';
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.PERSONALITY: return '[p]';
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    default: throw 'Unknown type error: ' + speechType;
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Defines a component within a speech rule.
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {{type: cvox.SpeechRule.Type, content: string}} kwargs The input
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * component in JSON format.
110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor
111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Component = function(kwargs) {
113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {cvox.SpeechRule.Type} */
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.type = kwargs.type;
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {string} */
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.content = kwargs.content;
118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses a valid string representation of a speech component into a Component
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * object.
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} input The input string.
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {cvox.SpeechRule.Component} The resulting component.
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Component.fromString = function(input) {
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // The output JSON.
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var output = {};
130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Parse the type.
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  output.type = cvox.SpeechRule.Type.fromString(input.substring(0, 3));
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Prep the rest of the parsing.
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var rest = input.slice(3).trimLeft();
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!rest) {
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    throw new cvox.SpeechRule.OutputError('Missing content.');
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  switch (output.type) {
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.TEXT:
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if (rest[0] == '"') {
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        var quotedString = cvox.SpeechRule.splitString_(rest, '\\(')[0].trim();
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (quotedString.slice(-1) != '"') {
145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          throw new cvox.SpeechRule.OutputError('Invalid string syntax.');
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        output.content = quotedString;
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        rest = rest.slice(quotedString.length).trim();
149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        if (rest.indexOf('(') == -1) {
150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          rest = '';
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        }
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        // This break is conditional. If the content is not an explicit string,
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        // it can be treated like node and multi type.
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        break;
155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.NODE:
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case cvox.SpeechRule.Type.MULTI:
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      var bracket = rest.indexOf(' (');
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if (bracket == -1) {
160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        output.content = rest.trim();
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        rest = '';
162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        break;
163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      output.content = rest.substring(0, bracket).trim();
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      rest = rest.slice(bracket).trimLeft();
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    break;
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  output = new cvox.SpeechRule.Component(output);
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (rest) {
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    output.addAttributes(rest);
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return output;
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @override
178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Component.prototype.toString = function() {
180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var strs = '';
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  strs += cvox.SpeechRule.Type.toString(this.type);
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  strs += this.content ? ' ' +  this.content : '';
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var attribs = this.getAttributes();
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (attribs.length > 0) {
185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    strs += ' (' + attribs.join(', ') + ')';
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return strs;
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Adds a single attribute to the component.
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} attr String representation of an attribute.
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Component.prototype.addAttribute = function(attr) {
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var colon = attr.indexOf(':');
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (colon == -1) {
198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    this[attr.trim()] = 'true';
199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  } else {
200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    this[attr.substring(0, colon).trim()] = attr.slice(colon + 1).trim();
201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Adds a list of attributes to the component.
207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} attrs String representation of attribute list.
208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Component.prototype.addAttributes = function(attrs) {
210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (attrs[0] != '(' || attrs.slice(-1) != ')') {
211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    throw new cvox.SpeechRule.OutputError(
212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        'Invalid attribute expression: ' + attrs);
213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var attribs = cvox.SpeechRule.splitString_(attrs.slice(1, -1), ',');
215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (var i = 0; i < attribs.length; i++) {
216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    this.addAttribute(attribs[i]);
217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
220cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
221cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Transforms the attributes of an object into a list of strings.
223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {Array.<string>} List of translated attribute:value strings.
224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Component.prototype.getAttributes = function() {
226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var attribs = [];
227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (var key in this) {
228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (key != 'content' && key != 'type' && typeof(this[key]) != 'function') {
229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      attribs.push(key + ':' + this[key]);
230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return attribs;
233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A speech rule is a collection of speech components.
238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Array.<cvox.SpeechRule.Component>} components The input rule.
239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor
240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Action = function(components) {
242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {Array.<cvox.SpeechRule.Component>} */
243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.components = components;
244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses an input string into a speech rule class object.
249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} input The input string.
250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {cvox.SpeechRule.Action} The resulting object.
251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Action.fromString = function(input) {
253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var comps = cvox.SpeechRule.splitString_(input, ';')
254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      .filter(function(x) {return x.match(/\S/);})
255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      .map(function(x) {return x.trim();});
256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var newComps = [];
257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (var i = 0; i < comps.length; i++) {
258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var comp = cvox.SpeechRule.Component.fromString(comps[i]);
259cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (comp) {
260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      newComps.push(comp);
261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)return new cvox.SpeechRule.Action(newComps);
264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
265cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
267cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
268cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @override
269cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
270cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Action.prototype.toString = function() {
271cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var comps = this.components.map(function(c) { return c.toString(); });
272cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return comps.join('; ');
273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
274cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
275cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
276cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// TODO (sorge) Separatation of xpath expressions and custom functions.
277cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Also test validity of xpath expressions.
278cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
279cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Constructs a valid precondition for a speech rule.
280cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} query A node selector function or xpath expression.
281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Array.<string>=} opt_constraints A list of constraint functions.
282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor
283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Precondition = function(query, opt_constraints) {
285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {string} */
286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.query = query;
287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /** @type {!Array.<string>} */
289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.constraints = opt_constraints || [];
290cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
291cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @override
295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.Precondition.prototype.toString = function() {
297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var constrs = this.constraints.join(', ');
298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return this.query + ', ' + constrs;
299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
301cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
302cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Split a string wrt. a given separator symbol while not splitting inside of a
304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * double quoted string. For example, splitting
305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * '[t] "matrix; 3 by 3"; [n] ./*[1]' with separators ';' would yield
306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * ['[t] "matrix; 3 by 3"', ' [n] ./*[1]'].
307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} str String to be split.
308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} sep Separator symbol.
309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {Array.<string>} A list of single component strings.
310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private
311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.splitString_ = function(str, sep) {
313cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var strList = [];
314cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var prefix = '';
315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  while (str != '') {
317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var sepPos = str.search(sep);
318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (sepPos == -1) {
319cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if ((str.match(/"/g) || []).length % 2 != 0) {
320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        throw new cvox.SpeechRule.OutputError(
321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            'Invalid string in expression: ' + str);
322cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      strList.push(prefix + str);
324cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      prefix = '';
325cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      str = '';
326cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else if (
327cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        (str.substring(0, sepPos).match(/"/g) || []).length % 2 == 0) {
328cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      strList.push(prefix + str.substring(0, sepPos));
329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      prefix = '';
330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      str = str.substring(sepPos + 1);
331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else {
332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      var nextQuot = str.substring(sepPos).search('"');
333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if (nextQuot == -1) {
334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        throw new cvox.SpeechRule.OutputError(
335cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            'Invalid string in expression: ' + str);
336cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      } else {
337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        prefix = prefix + str.substring(0, sepPos + nextQuot + 1);
338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        str = str.substring(sepPos + nextQuot + 1);
339cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
340cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (prefix) {
343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    strList.push(prefix);
344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
345cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return strList;
346cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
347cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
348cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
349cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
350cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Attributes for dynamic constraints.
351cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * We define one default attribute as style. Speech rule stores can add other
352cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * attributes later.
353cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @enum {string}
354cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
355cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.DynamicCstrAttrib =
356cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  STYLE: 'style'
358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
359cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
360cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
361cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Dynamic constraints are a means to specialize rules that can be changed
363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * dynamically by the user, for example by choosing different styles, etc.
364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @typedef {!Object.<cvox.SpeechRule.DynamicCstrAttrib, string>}
365cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
366cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.DynamicCstr;
367cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
368cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
369cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/**
370cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Error object for signaling parsing errors.
371cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} msg The error message.
372cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor
373cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @extends {Error}
374cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
375cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.SpeechRule.OutputError = function(msg) {
376cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.name = 'RuleError';
377cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  this.message = msg || '';
378cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.inherits(cvox.SpeechRule.OutputError, Error);
380