Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / common / math_semantic_tree.js
blob991ae3244caf6d562f09ebd9b5342c50750ae8b5
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.
5 /**
6  * @fileoverview A semantic tree for MathML expressions.
7  *
8  * This file contains functionality to compute a semantic interpretation from a
9  * given MathML expression. This is a very heuristic approach that assumes a
10  * fairly simple default semantic which is suitable for K-12 and simple UG
11  * mathematics.
12  *
13  */
15 goog.provide('cvox.SemanticTree');
16 goog.provide('cvox.SemanticTree.Node');
18 goog.require('cvox.DomUtil');
19 goog.require('cvox.SemanticAttr');
20 goog.require('cvox.SemanticUtil');
23 /**
24  * Create an initial semantic tree.
25  * @param {!Element} mml The original MathML node.
26  * @constructor
27  */
28 cvox.SemanticTree = function(mml) {
29   /** ID counter.
30    * @type {number}
31    * @private
32    */
33   this.idCounter_ = 0;
35   /** Original MathML tree.
36    * @type {Node}
37    */
38   this.mathml = mml;
40   /** @type {cvox.SemanticTree.Node} */
41   this.root = this.parseMathml_(mml);
45 /**
46  * @param {number} id Node id.
47  * @constructor
48  */
49 cvox.SemanticTree.Node = function(id) {
50   /** @type {number} */
51   this.id = id;
53   /** @type {Array<Element>} */
54   this.mathml = [];
56   /** @type {cvox.SemanticTree.Node} */
57   this.parent = null;
59   /** @type {cvox.SemanticAttr.Type} */
60   this.type = cvox.SemanticAttr.Type.UNKNOWN;
62   /** @type {cvox.SemanticAttr.Role} */
63   this.role = cvox.SemanticAttr.Role.UNKNOWN;
65   /** @type {cvox.SemanticAttr.Font} */
66   this.font = cvox.SemanticAttr.Font.UNKNOWN;
68   /** @type {!Array<cvox.SemanticTree.Node>} */
69   this.childNodes = [];
71   /** @type {string} */
72   this.textContent = '';
74   /** Branch nodes can store additional nodes that can be useful.
75    * E.g. a node of type FENCED can have the opening and closing fences here.
76    * @type {!Array<cvox.SemanticTree.Node>}
77    */
78   this.contentNodes = [];
82 /**
83  * Retrieve all subnodes (including the node itself) that satisfy a given
84  * predicate.
85  * @param {function(cvox.SemanticTree.Node): boolean} pred The predicate.
86  * @return {!Array<cvox.SemanticTree.Node>} The nodes in the tree for which the
87  *     predicate holds.
88  */
89 cvox.SemanticTree.Node.prototype.querySelectorAll = function(pred) {
90   var result = [];
91   for (var i = 0, child; child = this.childNodes[i]; i++) {
92     result = result.concat(child.querySelectorAll(pred));
93   }
94   if (pred(this)) {
95     result.unshift(this);
96   }
97   return result;
101  /**
102   * Returns an XML representation of the tree.
103   * @param {boolean=} brief If set attributes are omitted.
104   * @return {Node} The XML representation of the tree.
105   */
106  cvox.SemanticTree.prototype.xml = function(brief) {
107    var dp = new DOMParser();
108    var xml = dp.parseFromString('<stree></stree>', 'text/xml');
110    var xmlRoot = this.root.xml(xml, brief);
111    xml.childNodes[0].appendChild(xmlRoot);
113    return xml.childNodes[0];
114  };
117  /**
118   * An XML tree representation of the current node.
119   * @param {Document} xml The XML document.
120   * @param {boolean=} brief If set attributes are omitted.
121   * @return {Node} The XML representation of the node.
122   */
123  cvox.SemanticTree.Node.prototype.xml = function(xml, brief) {
124    /**
125     * Translates a list of nodes into XML representation.
126     * @param {string} tag Name of the enclosing tag.
127     * @param {!Array<!cvox.SemanticTree.Node>} nodes A list of nodes.
128     * @return {Node} An XML representation of the node list.
129     */
130    var xmlNodeList = function(tag, nodes) {
131      var xmlNodes = nodes.map(function(x) {return x.xml(xml, brief);});
132      var tagNode = xml.createElement(tag);
133      for (var i = 0, child; child = xmlNodes[i]; i++) {
134        tagNode.appendChild(child);
135      }
136      return tagNode;
137    };
138    var node = xml.createElement(this.type);
139    if (!brief) {
140      this.xmlAttributes_(node);
141    }
142    node.textContent = this.textContent;
143    if (this.contentNodes.length > 0) {
144      node.appendChild(xmlNodeList('content', this.contentNodes));
145    }
146    if (this.childNodes.length > 0) {
147      node.appendChild(xmlNodeList('children', this.childNodes));
148    }
149    return node;
150  };
154   * Serializes the XML representation of the tree.
155   * @param {boolean=} brief If set attributes are omitted.
156  * @return {string} Serialized string.
157  */
158 cvox.SemanticTree.prototype.toString = function(brief) {
159   var xmls = new XMLSerializer();
160   return xmls.serializeToString(this.xml(brief));
165  * Pretty print the XML representation of the tree.
166  * @param {boolean=} brief If set attributes are omitted.
167  * @return {string} The formatted string.
168  */
169 cvox.SemanticTree.prototype.formatXml = function(brief) {
170   var xml = this.toString(brief);
171   return cvox.SemanticTree.formatXml(xml);
176  * Pretty prints an XML representation.
177  * @param {string} xml The serialised XML string.
178  * @return {string} The formatted string.
179  */
180 cvox.SemanticTree.formatXml = function(xml) {
181   var reg = /(>)(<)(\/*)/g;
182   xml = xml.replace(reg, '$1\r\n$2$3');
183   reg = /(>)(.+)(<c)/g;
184   xml = xml.replace(reg, '$1\r\n$2\r\n$3');
185   var formatted = '';
186   var padding = '';
187   xml.split('\r\n')
188       .forEach(function(node) {
189                  if (node.match(/.+<\/\w[^>]*>$/)) {
190                    // Node with content.
191                    formatted += padding + node + '\r\n';
192                  } else if (node.match(/^<\/\w/)) {
193                    if (padding) {
194                      // Closing tag
195                      padding = padding.slice(2);
196                      formatted += padding + node + '\r\n';
197                    }
198                  } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
199                    // Opening tag
200                    formatted += padding + node + '\r\n';
201                    padding += '  ';
202                  } else {
203                    // Empty tag
204                    formatted += padding + node + '\r\n';
205                  }
206                });
207   return formatted;
212  * Serializes the XML representation of a node.
213  * @param {boolean=} brief If attributes are to be omitted.
214  * @return {string} Serialized string.
215  */
216 cvox.SemanticTree.Node.prototype.toString = function(brief) {
217   var xmls = new XMLSerializer();
218   var dp = new DOMParser();
219   var xml = dp.parseFromString('', 'text/xml');
220   return xmls.serializeToString(this.xml(xml, brief));
225  * Adds attributes to the XML representation of the current node.
226  * @param {Node} node The XML node.
227  * @private
228  */
229 cvox.SemanticTree.Node.prototype.xmlAttributes_ = function(node) {
230   node.setAttribute('role', this.role);
231   if (this.font != cvox.SemanticAttr.Font.UNKNOWN) {
232     node.setAttribute('font', this.font);
233   }
234   node.setAttribute('id', this.id);
238 /** Creates a new node object.
239  * @return {cvox.SemanticTree.Node} The newly created node.
240  * @private
241  */
242 cvox.SemanticTree.prototype.createNode_ = function() {
243   return new cvox.SemanticTree.Node(this.idCounter_++);
248  * Replaces a node in the tree. Updates the root node if necessary.
249  * @param {!cvox.SemanticTree.Node} oldNode The node to be replaced.
250  * @param {!cvox.SemanticTree.Node} newNode The new node.
251  * @private
252  */
253 cvox.SemanticTree.prototype.replaceNode_ = function(oldNode, newNode) {
254   var parent = oldNode.parent;
255   if (!parent) {
256     this.root = newNode;
257     return;
258   }
259   parent.replaceChild_(oldNode, newNode);
264  * Updates the content of the node thereby possibly changing type and role.
265  * @param {string} content The new content string.
266  * @private
267  */
268 cvox.SemanticTree.Node.prototype.updateContent_ = function(content) {
269   // Remove superfluous whitespace!
270   content = content.trim();
271   if (this.textContent == content) {
272     return;
273   }
274   var meaning = cvox.SemanticAttr.lookupMeaning(content);
275   this.textContent = content;
276   this.role = meaning.role;
277   this.type = meaning.type;
278   this.font = meaning.font;
283  * Adds MathML nodes to the node's store of MathML nodes if necessary only, as
284  * we can not necessarily assume that the MathML of the content nodes and
285  * children are all disjoint.
286  * @param {Array<Node>} mmlNodes List of MathML nodes.
287  * @private
288  */
289 cvox.SemanticTree.Node.prototype.addMathmlNodes_ = function(mmlNodes) {
290   for (var i = 0, mml; mml = mmlNodes[i]; i++) {
291     if (this.mathml.indexOf(mml) == -1) {
292       this.mathml.push(mml);
293     }
294   }
299  * Removes MathML nodes from the node's store of MathML nodes.
300  * @param {Array<Node>} mmlNodes List of MathML nodes.
301  * @private
302  */
303 cvox.SemanticTree.Node.prototype.removeMathmlNodes_ = function(mmlNodes) {
304   var mmlList = this.mathml;
305   for (var i = 0, mml; mml = mmlNodes[i]; i++) {
306     var index = mmlList.indexOf(mml);
307     if (index != -1) {
308       mmlList.splice(index, 1);
309     }
310   }
311   this.mathml = mmlList;
316  * Appends a child to the node.
317  * @param {cvox.SemanticTree.Node} child The new child.
318  * @private
319  */
320 cvox.SemanticTree.Node.prototype.appendChild_ = function(child) {
321   this.childNodes.push(child);
322   this.addMathmlNodes_(child.mathml);
323   child.parent = this;
328  * Replaces a child node of the node.
329  * @param {!cvox.SemanticTree.Node} oldNode The node to be replaced.
330  * @param {!cvox.SemanticTree.Node} newNode The new node.
331  * @private
332  */
333 cvox.SemanticTree.Node.prototype.replaceChild_ = function(oldNode, newNode) {
334   var index = this.childNodes.indexOf(oldNode);
335   if (index == -1) {
336     return;
337   }
338   newNode.parent = this;
339   oldNode.parent = null;
340   this.childNodes[index] = newNode;
341   // To not mess up the order of MathML elements more than necessary, we only
342   // remove and add difference lists. The hope is that we might end up with
343   // little change.
344   var removeMathml = oldNode.mathml.filter(
345       function(x) {return newNode.mathml.indexOf(x) == -1;});
346   var addMathml = newNode.mathml.filter(
347       function(x) {return oldNode.mathml.indexOf(x) == -1;});
348   this.removeMathmlNodes_(removeMathml);
349   this.addMathmlNodes_(addMathml);
354  * Appends a content node to the node.
355  * @param {cvox.SemanticTree.Node} node The new content node.
356  * @private
357  */
358 cvox.SemanticTree.Node.prototype.appendContentNode_ = function(node) {
359   if (node) {
360     this.contentNodes.push(node);
361     this.addMathmlNodes_(node.mathml);
362     node.parent = this;
363   }
368  * Removes a content node from the node.
369  * @param {cvox.SemanticTree.Node} node The content node to be removed.
370  * @private
371  */
372 cvox.SemanticTree.Node.prototype.removeContentNode_ = function(node) {
373   if (node) {
374     var index = this.contentNodes.indexOf(node);
375     if (index != -1) {
376       this.contentNodes.splice(index, 1);
377     }
378   }
383  * This is the main function that creates the semantic tree by recursively
384  * parsing the initial MathML tree and bottom up assembling the tree.
385  * @param {!Element} mml The MathML tree.
386  * @return {!cvox.SemanticTree.Node} The root of the new tree.
387  * @private
388  */
389 cvox.SemanticTree.prototype.parseMathml_ = function(mml) {
390   var children = cvox.DomUtil.toArray(mml.children);
391   switch (cvox.SemanticUtil.tagName(mml)) {
392     case 'MATH':
393     case 'MROW':
394     case 'MPADDED':
395     case 'MSTYLE':
396       children = cvox.SemanticUtil.purgeNodes(children);
397       // Single child node, i.e. the row is meaningless.
398       if (children.length == 1) {
399         return this.parseMathml_(/** @type {!Element} */(children[0]));
400       }
401       // Case of a 'meaningful' row, even if they are empty.
402       return this.processRow_(this.parseMathmlChildren_(children));
403     break;
404     case 'MFRAC':
405       var newNode = this.makeBranchNode_(
406           cvox.SemanticAttr.Type.FRACTION,
407           [this.parseMathml_(children[0]), this.parseMathml_(children[1])],
408           []);
409       newNode.role = cvox.SemanticAttr.Role.DIVISION;
410       return newNode;
411       break;
412     case 'MSUB':
413     case 'MSUP':
414     case 'MSUBSUP':
415     case 'MOVER':
416     case 'MUNDER':
417     case 'MUNDEROVER':
418       return this.makeLimitNode_(cvox.SemanticUtil.tagName(mml),
419                                  this.parseMathmlChildren_(children));
420       break;
421     case 'MROOT':
422       return this.makeBranchNode_(
423           cvox.SemanticAttr.Type.ROOT,
424           [this.parseMathml_(children[0]), this.parseMathml_(children[1])],
425           []);
426       break;
427     case 'MSQRT':
428       children = this.parseMathmlChildren_(
429           cvox.SemanticUtil.purgeNodes(children));
430       return this.makeBranchNode_(
431           cvox.SemanticAttr.Type.SQRT, [this.processRow_(children)], []);
432       break;
433     case 'MTABLE':
434       newNode = this.makeBranchNode_(
435           cvox.SemanticAttr.Type.TABLE,
436           this.parseMathmlChildren_(children), []);
437       if (cvox.SemanticTree.tableIsMultiline_(newNode)) {
438         this.tableToMultiline_(newNode);
439         }
440       return newNode;
441       break;
442     case 'MTR':
443       newNode = this.makeBranchNode_(
444           cvox.SemanticAttr.Type.ROW,
445           this.parseMathmlChildren_(children), []);
446       newNode.role = cvox.SemanticAttr.Role.TABLE;
447       return newNode;
448       break;
449     case 'MTD':
450       children = this.parseMathmlChildren_(
451           cvox.SemanticUtil.purgeNodes(children));
452       newNode = this.makeBranchNode_(
453           cvox.SemanticAttr.Type.CELL, [this.processRow_(children)], []);
454       newNode.role = cvox.SemanticAttr.Role.TABLE;
455       return newNode;
456       break;
457     case 'MTEXT':
458       var leaf = this.makeLeafNode_(mml);
459       leaf.type = cvox.SemanticAttr.Type.TEXT;
460       return leaf;
461       break;
462     // TODO (sorge) Role and font of multi-character and digits unicode strings.
463     // TODO (sorge) Reclassify wrongly tagged numbers or identifiers.
464     // TODO (sorge) Put this all in a single clean reclassification method.
465     case 'MI':
466       leaf = this.makeLeafNode_(mml);
467       if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) {
468         leaf.type = cvox.SemanticAttr.Type.IDENTIFIER;
469       }
470       return leaf;
471       break;
472     case 'MN':
473       leaf = this.makeLeafNode_(mml);
474       if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) {
475         leaf.type = cvox.SemanticAttr.Type.NUMBER;
476       }
477       return leaf;
478       break;
479     case 'MO':
480       leaf = this.makeLeafNode_(mml);
481       if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) {
482         leaf.type = cvox.SemanticAttr.Type.OPERATOR;
483       }
484       return leaf;
485       break;
486     // TODO (sorge) Do something useful with error and phantom symbols.
487     default:
488     // Ordinarilly at this point we should not get any other tag.
489       return this.makeUnprocessed_(mml);
490   }
495  * Parse a list of MathML nodes into the semantic tree.
496  * @param {Array<Element>} mmls A list of MathML nodes.
497  * @return {!Array<cvox.SemanticTree.Node>} The list of resulting semantic
498  *     node.
499  * @private
500  */
501 cvox.SemanticTree.prototype.parseMathmlChildren_ = function(mmls) {
502   var result = [];
503   for (var i = 0, mml; mml = mmls[i]; i++) {
504     result.push(this.parseMathml_(mml));
505   }
506   return result;
510  * Create a node that is to be processed at a later point in time.
511  * @param {Node} mml The MathML tree.
512  * @return {!cvox.SemanticTree.Node} The new node.
513  * @private
514  */
515 cvox.SemanticTree.prototype.makeUnprocessed_ = function(mml) {
516   var node = this.createNode_();
517   node.mathml = [mml];
518   return node;
523  * Create an empty leaf node.
524  * @return {!cvox.SemanticTree.Node} The new node.
525  * @private
526  */
527 cvox.SemanticTree.prototype.makeEmptyNode_ = function() {
528   var node = this.createNode_();
529   node.type = cvox.SemanticAttr.Type.EMPTY;
530   return node;
535  * Create a leaf node.
536  * @param {Node} mml The MathML tree.
537  * @return {!cvox.SemanticTree.Node} The new node.
538  * @private
539  */
540 cvox.SemanticTree.prototype.makeLeafNode_ = function(mml) {
541   var node = this.createNode_();
542   node.mathml = [mml];
543   node.updateContent_(mml.textContent);
544   node.font = mml.getAttribute('mathvariant') || node.font;
545   return node;
550  * Create a branching node.
551  * @param {!cvox.SemanticAttr.Type} type The type of the node.
552  * @param {!Array<cvox.SemanticTree.Node>} children The child nodes.
553  * @param {!Array<cvox.SemanticTree.Node>} contentNodes The content Nodes.
554  * @param {string=} content Content string if there is any.
555  * @return {!cvox.SemanticTree.Node} The new node.
556  * @private
557  */
558 cvox.SemanticTree.prototype.makeBranchNode_ = function(
559     type, children, contentNodes, content) {
560   var node = this.createNode_();
561   if (content) {
562     node.updateContent_(content);
563   }
564   node.type = type;
565   node.childNodes = children;
566   node.contentNodes = contentNodes;
567   children.concat(contentNodes)
568       .forEach(
569           function(x) {
570             x.parent = node;
571             node.addMathmlNodes_(x.mathml);
572           });
573   return node;
578  * Create a branching node for an implicit operation, currently assumed to
579  * be of multiplicative type.
580  * @param {!Array<!cvox.SemanticTree.Node>} nodes The operands.
581  * @return {!cvox.SemanticTree.Node} The new branch node.
582  * @private
583  */
584 cvox.SemanticTree.prototype.makeImplicitNode_ = function(nodes) {
585   if (nodes.length == 1) {
586     return nodes[0];
587     }
588   var operator = this.createNode_();
589   // For now we assume this is a multiplication using invisible times.
590   operator.updateContent_(cvox.SemanticAttr.invisibleTimes());
591   var newNode = this.makeInfixNode_(nodes, operator);
592   newNode.role = cvox.SemanticAttr.Role.IMPLICIT;
593   return newNode;
598  * Create a branching node for an infix operation.
599  * @param {!Array<cvox.SemanticTree.Node>} children The operands.
600  * @param {!cvox.SemanticTree.Node} opNode The operator.
601  * @return {!cvox.SemanticTree.Node} The new branch node.
602  * @private
603  */
604 cvox.SemanticTree.prototype.makeInfixNode_ = function(children, opNode) {
605   return this.makeBranchNode_(
606       cvox.SemanticAttr.Type.INFIXOP, children, [opNode], opNode.textContent);
611  * Creates a node of the specified type by collapsing the given node list into
612  * one content (thereby concatenating the content of each node into a single
613  * content string) with the inner node as a child.
614  * @param {!cvox.SemanticTree.Node} inner The inner node.
615  * @param {!Array<cvox.SemanticTree.Node>} nodeList List of nodes.
616  * @param {!cvox.SemanticAttr.Type} type The new type of the node.
617  * @return {!cvox.SemanticTree.Node} The new branch node.
618  * @private
619  */
620 cvox.SemanticTree.prototype.makeConcatNode_ = function(inner, nodeList, type) {
621   if (nodeList.length == 0) {
622     return inner;
623   }
624   var content = nodeList.map(function(x) {return x.textContent;}).join(' ');
625   var newNode = this.makeBranchNode_(type, [inner], nodeList, content);
626   if (nodeList.length > 0) {
627     newNode.role = cvox.SemanticAttr.Role.MULTIOP;
628   }
629   return newNode;
634  * Wraps a node into prefix operators.
635  * Example: + - a becomes (+ (- (a)))
636  * Input: a  [+, -] ->  Output: content: '+ -', child: a
637  * @param {!cvox.SemanticTree.Node} node The inner node.
638  * @param {!Array<cvox.SemanticTree.Node>} prefixes Prefix operators
639  * from the outermost to the innermost.
640  * @return {!cvox.SemanticTree.Node} The new branch node.
641  * @private
642  */
643 cvox.SemanticTree.prototype.makePrefixNode_ = function(node, prefixes) {
644   var negatives = cvox.SemanticTree.partitionNodes_(
645       prefixes, cvox.SemanticTree.attrPred_('role', 'SUBTRACTION'));
646   var newNode = this.makeConcatNode_(
647       node, negatives.comp.pop(), cvox.SemanticAttr.Type.PREFIXOP);
649   while (negatives.rel.length > 0) {
650     newNode = this.makeConcatNode_(
651         newNode, [negatives.rel.pop()], cvox.SemanticAttr.Type.PREFIXOP);
652     newNode.role = cvox.SemanticAttr.Role.NEGATIVE;
653     newNode = this.makeConcatNode_(
654         newNode, negatives.comp.pop(), cvox.SemanticAttr.Type.PREFIXOP);
655   }
656   return newNode;
661  * Wraps a node into postfix operators.
662  * Example: a - + becomes (((a) -) +)
663  * Input: a  [-, +] ->  Output: content: '- +', child: a
664  * @param {!cvox.SemanticTree.Node} node The inner node.
665  * @param {!Array<cvox.SemanticTree.Node>} postfixes Postfix operators from
666  * innermost to outermost.
667  * @return {!cvox.SemanticTree.Node} The new branch node.
668  * @private
669  */
670 cvox.SemanticTree.prototype.makePostfixNode_ = function(node, postfixes) {
671   return this.makeConcatNode_(
672       node, postfixes, cvox.SemanticAttr.Type.POSTFIXOP);
676 // TODO (sorge) Separate out interspersed text before the relations in row
677 // heuristic otherwise we get them as implicit operations!
678 // Currently we handle that later in the rules, which is rather messy.
680  * Processes a list of nodes, combining expressions by delimiters, tables,
681  * punctuation sequences, function/big operator/integral applications to
682  * generate a syntax tree with relation and operator precedence.
684  * This is the main heuristic to rewrite a flat row of terms into a meaningful
685  * term tree.
686  * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes.
687  * @return {!cvox.SemanticTree.Node} The root node of the syntax tree.
688  * @private
689  */
690 cvox.SemanticTree.prototype.processRow_ = function(nodes) {
691   if (nodes.length == 0) {
692     return this.makeEmptyNode_();
693   }
694   nodes = this.getFencesInRow_(nodes);
695   nodes = this.processTablesInRow_(nodes);
696   nodes = this.getPunctuationInRow_(nodes);
697   nodes = this.getFunctionsInRow_(nodes);
698   return this.processRelationsInRow_(nodes);
703  * Constructs a syntax tree with relation and operator precedence from a list
704  * of nodes.
705  * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes.
706  * @return {!cvox.SemanticTree.Node} The root node of the syntax tree.
707  * @private
708  */
709 cvox.SemanticTree.prototype.processRelationsInRow_ = function(nodes) {
710   var partition = cvox.SemanticTree.partitionNodes_(
711       nodes, cvox.SemanticTree.attrPred_('type', 'RELATION'));
712   var firstRel = partition.rel[0];
714   if (!firstRel) {
715     return this.processOperationsInRow_(nodes);
716   }
717   if (nodes.length == 1) {
718     return nodes[0];
719   }
720   var children = partition.comp.map(
721       goog.bind(this.processOperationsInRow_, this));
722   if (partition.rel.every(
723          function(x) {return x.textContent == firstRel.textContent;})) {
724     return this.makeBranchNode_(
725         cvox.SemanticAttr.Type.RELSEQ, children, partition.rel,
726         firstRel.textContent);
727   }
728   return this.makeBranchNode_(
729       cvox.SemanticAttr.Type.MULTIREL, children, partition.rel);
734  * Constructs a syntax tree with operator precedence from a list nodes.
735  * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes.
736  * @return {!cvox.SemanticTree.Node} The root node of the syntax tree.
737  * @private
738  */
739 cvox.SemanticTree.prototype.processOperationsInRow_ = function(nodes) {
740   if (nodes.length == 0) {
741     return this.makeEmptyNode_();
742   }
743   if (nodes.length == 1) {
744     return nodes[0];
745   }
747   var prefix = [];
748   while (nodes.length > 0 &&
749       nodes[0].type == cvox.SemanticAttr.Type.OPERATOR) {
750     prefix.push(nodes.shift());
751   }
752   // Pathological case: only operators in row.
753   if (nodes.length == 0) {
754     return this.makePrefixNode_(prefix.pop(), prefix);
755   }
756   if (nodes.length == 1) {
757     return this.makePrefixNode_(nodes[0], prefix);
758   }
760   var split = cvox.SemanticTree.sliceNodes_(
761       nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR'));
762   // At this point, we know that split.head is not empty!
763   var node = this.makePrefixNode_(
764       this.makeImplicitNode_(
765           /** @type {!Array<!cvox.SemanticTree.Node>} */ (split.head)),
766       prefix);
767   if (!split.div) {
768     return node;
769   }
770   return this.makeOperationsTree_(split.tail, node, split.div);
775  * Recursively constructs syntax tree with operator precedence from a list nodes
776  * given a initial root node.
777  * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes.
778  * @param {!cvox.SemanticTree.Node} root Initial tree.
779  * @param {!cvox.SemanticTree.Node} lastop Last operator that has not been
780  * processed yet.
781  * @param {Array<cvox.SemanticTree.Node>=} prefixes Operator nodes that will
782  * become prefix operation (or postfix in case they come after last operand).
783  * @return {!cvox.SemanticTree.Node} The root node of the syntax tree.
784  * @private
785  */
786 cvox.SemanticTree.prototype.makeOperationsTree_ = function(
787     nodes, root, lastop, prefixes) {
788   prefixes = prefixes || [];
790   if (nodes.length == 0) {
791     // Left over prefixes become postfixes.
792     prefixes.unshift(lastop);
793     if (root.type == cvox.SemanticAttr.Type.INFIXOP) {
794       // We assume prefixes bind stronger than postfixes.
795       var node = this.makePostfixNode_(
796           // Here we know that the childNodes are not empty!
797           /** @type {!cvox.SemanticTree.Node} */ (root.childNodes.pop()),
798           prefixes);
799       root.appendChild_(node);
800       return root;
801     }
802     return this.makePostfixNode_(root, prefixes);
803   }
805   var split = cvox.SemanticTree.sliceNodes_(
806       nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR'));
808   if (split.head.length == 0) {
809     prefixes.push(split.div);
810     return this.makeOperationsTree_(split.tail, root, lastop, prefixes);
811   }
813   var node = this.makePrefixNode_(
814       this.makeImplicitNode_(split.head), prefixes);
815   var newNode = this.appendOperand_(root, lastop, node);
816   if (!split.div) {
817     return newNode;
818   }
820   return this.makeOperationsTree_(split.tail, newNode, split.div, []);
823 // TODO (sorge) The following four functions could be combined into
824 // a single one. Currently it is clearer the way it is, though.
826  * Appends an operand at the right place in an operator tree.
827  * @param {!cvox.SemanticTree.Node} root The operator tree.
828  * @param {!cvox.SemanticTree.Node} op The operator node.
829  * @param {!cvox.SemanticTree.Node} node The node to be added.
830  * @return {!cvox.SemanticTree.Node} The modified root node.
831  * @private
832  */
833 cvox.SemanticTree.prototype.appendOperand_ = function(root, op, node) {
834   // In general our operator tree will have the form that additions and
835   // subtractions are stacked, while multiplications are subordinate.
836   if (root.type != cvox.SemanticAttr.Type.INFIXOP) {
837     return this.makeInfixNode_([root, node], op);
838   }
839   if (this.appendExistingOperator_(root, op, node)) {
840     return root;
841   }
842   return op.role == cvox.SemanticAttr.Role.MULTIPLICATION ?
843       this.appendMultiplicativeOp_(root, op, node) :
844           this.appendAdditiveOp_(root, op, node);
849  * Appends a multiplicative operator and operand.
850  * @param {!cvox.SemanticTree.Node} root The root node.
851  * @param {!cvox.SemanticTree.Node} op The operator node.
852  * @param {!cvox.SemanticTree.Node} node The operand node to be added.
853  * @return {!cvox.SemanticTree.Node} The modified root node.
854  * @private
855  */
856 cvox.SemanticTree.prototype.appendMultiplicativeOp_ = function(root, op, node) {
857   var lastRoot = root;
858   var lastChild = root.childNodes[root.childNodes.length - 1];
859   while (lastChild && lastChild.type == cvox.SemanticAttr.Type.INFIXOP) {
860     lastRoot = lastChild;
861     lastChild = lastRoot.childNodes[root.childNodes.length - 1];
862   }
863   var newNode = this.makeInfixNode_([lastRoot.childNodes.pop(), node], op);
864   lastRoot.appendChild_(newNode);
865   return root;
870  * Appends an additive/substractive operator and operand.
871  * @param {!cvox.SemanticTree.Node} root The old root node.
872  * @param {!cvox.SemanticTree.Node} op The operator node.
873  * @param {!cvox.SemanticTree.Node} node The operand node to be added.
874  * @return {!cvox.SemanticTree.Node} The new root node.
875  * @private
876  */
877 cvox.SemanticTree.prototype.appendAdditiveOp_ = function(root, op, node) {
878   return this.makeInfixNode_([root, node], op);
883  * Adds an operand to an operator node if it is the continuation of an existing
884  * operation.
885  * @param {!cvox.SemanticTree.Node} root The root node.
886  * @param {!cvox.SemanticTree.Node} op The operator node.
887  * @param {!cvox.SemanticTree.Node} node The operand node to be added.
888  * @return {boolean} True if operator was successfully appended.
889  * @private
890  */
891 cvox.SemanticTree.prototype.appendExistingOperator_ = function(root, op, node) {
892   if (!root || root.type != cvox.SemanticAttr.Type.INFIXOP) {
893     return false;
894   }
895   if (root.textContent == op.textContent) {
896     root.appendContentNode_(op);
897     root.appendChild_(node);
898     return true;
899   }
900   this.appendExistingOperator_(
901       // Again, if this is an INFIXOP node, we know it has a child!
902       /** @type {!cvox.SemanticTree.Node} */
903       (root.childNodes[root.childNodes.length - 1]),
904       op, node);
908 // TODO (sorge) The following procedure needs a rational reconstruction. It
909 // contains a number of similar cases which should be combined.
911  * Combines delimited expressions in a list of nodes.
913  * The basic idea of the heuristic is as follows:
914  * 1. Opening and closing delimiters are matched regardless of the actual shape
915  *    of the fence. These are turned into fenced nodes.
916  * 2. Neutral fences are matched only with neutral fences of the same shape.
917  * 3. For a collection of unmatched neutral fences we try to get a maximum
918  *    number of matching fences. E.g. || a|b || would be turned into a fenced
919  *    node with fences || and content a|b.
920  * 4. Any remaining unmatched delimiters are turned into punctuation nodes.
921  * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes.
922  * @return {!Array<!cvox.SemanticTree.Node>} The new list of nodes.
923  * @private
924  */
925 cvox.SemanticTree.prototype.getFencesInRow_ = function(nodes) {
926   var partition = cvox.SemanticTree.partitionNodes_(
927       nodes, cvox.SemanticTree.attrPred_('type', 'FENCE'));
928   var felem = partition.comp.shift();
929   return this.processFences_(partition.rel, partition.comp, [], [felem]);
934  * Recursively processes a list of nodes and combines all the fenced expressions
935  * into single nodes. It also processes singular fences, building expressions
936  * that are only fenced left or right.
937  * @param {!Array<cvox.SemanticTree.Node>} fences FIFO queue of fence nodes.
938  * @param {!Array<Array<cvox.SemanticTree.Node>>} content FIFO queue content
939  *     between fences.
940  * @param {!Array<cvox.SemanticTree.Node>} openStack LIFO stack of open fences.
941  * @param {!Array<!Array<cvox.SemanticTree.Node>>} contentStack LIFO stack of
942  *     content between fences yet to be processed.
943  * @return {!Array<cvox.SemanticTree.Node>} A list of nodes with all fenced
944  *     expressions processed.
945  * @private
946  */
947 cvox.SemanticTree.prototype.processFences_ = function(
948   fences, content, openStack, contentStack) {
949   // Base case 1: Everything is used up.
950   if (fences.length == 0 && openStack.length == 0) {
951     return contentStack[0];
952     }
953   var openPred = cvox.SemanticTree.attrPred_('role', 'OPEN');
954   // Base case 2: Only open and neutral fences are left on the stack.
955   if (fences.length == 0) {
956     // Basic idea:
957     // - make punctuation nodes from open fences
958     // - combine as many neutral fences as possible, if the are not separated by
959     //   open fences.
960     // The idea is to allow for things like case statements etc. and not bury
961     // them inside a neutral fenced expression.
962     //
963     // 0. We process the list from left to right. Hence the first element on the
964     //    content stack are actually left most elements in the expression.
965     // 1. Slice at open fence.
966     // 2. On tail optimize for neutral fences.
967     // 3. Repeat until fence stack is exhausted.
968     // Push rightmost elements onto the result.
969     var result = contentStack.shift();
970     while (openStack.length > 0) {
971       if (openPred(openStack[0])) {
972         var firstOpen = openStack.shift();
973         cvox.SemanticTree.fenceToPunct_(firstOpen);
974         result.push(firstOpen);
975       } else {
976         var split = cvox.SemanticTree.sliceNodes_(openStack, openPred);
977         var cutLength = split.head.length - 1;
978         var innerNodes = this.processNeutralFences_(
979             split.head, contentStack.slice(0, cutLength));
980         contentStack = contentStack.slice(cutLength);
981         //var rightContent = contentStack.shift();
982         result.push.apply(result, innerNodes);
983         //result.push.apply(result, rightContent);
984         if (split.div) {
985           split.tail.unshift(split.div);
986         }
987         openStack = split.tail;
988       }
989       result.push.apply(result, contentStack.shift());
990     }
991     return result;
992   }
993   var lastOpen = openStack[openStack.length - 1];
994   var firstRole = fences[0].role;
995   // General opening case.
996   // Either we have an open fence.
997   if (firstRole == cvox.SemanticAttr.Role.OPEN ||
998       // Or we have a neutral fence that does not have a counter part.
999           (firstRole == cvox.SemanticAttr.Role.NEUTRAL &&
1000               (!lastOpen ||
1001                   fences[0].textContent != lastOpen.textContent))) {
1002     openStack.push(fences.shift());
1003     contentStack.push(content.shift());
1004     return this.processFences_(fences, content, openStack, contentStack);
1005   }
1006   // General closing case.
1007   if (lastOpen && (
1008       // Closing fence for some opening fence.
1009       (firstRole == cvox.SemanticAttr.Role.CLOSE &&
1010           lastOpen.role == cvox.SemanticAttr.Role.OPEN) ||
1011               // Netural fence with exact counter part.
1012               (firstRole == cvox.SemanticAttr.Role.NEUTRAL &&
1013                   fences[0].textContent == lastOpen.textContent))) {
1014     var fenced = this.makeHorizontalFencedNode_(
1015         openStack.pop(), fences.shift(), contentStack.pop());
1016     contentStack.push(contentStack.pop().concat([fenced], content.shift()));
1017     return this.processFences_(fences, content, openStack, contentStack);
1018   }
1019   // Closing with a neutral fence on the stack.
1020   if (lastOpen && firstRole == cvox.SemanticAttr.Role.CLOSE &&
1021       lastOpen.role == cvox.SemanticAttr.Role.NEUTRAL &&
1022           openStack.some(openPred)) {
1023     // Steps of the algorithm:
1024     // 1. Split list at right most opening bracket.
1025     // 2. Cut content list at corresponding length.
1026     // 3. Optimise the neutral fences.
1027     // 4. Make fenced node.
1028     //
1029     // Careful, this reverses openStack!
1030     var split = cvox.SemanticTree.sliceNodes_(openStack, openPred, true);
1031     // We know that
1032     // (a) div & tail exist,
1033     // (b) all are combined in this step into a single fenced node,
1034     // (c) head is the new openStack,
1035     // (d) the new contentStack is remainder of contentStack + new fenced node +
1036     // shift of content.
1037     var rightContent = contentStack.pop();
1038     var cutLength = contentStack.length - split.tail.length + 1;
1039     var innerNodes = this.processNeutralFences_(
1040         split.tail, contentStack.slice(cutLength));
1041     contentStack = contentStack.slice(0, cutLength);
1042     var fenced = this.makeHorizontalFencedNode_(
1043         split.div, fences.shift(),
1044         contentStack.pop().concat(innerNodes, rightContent));
1045     contentStack.push(contentStack.pop().concat([fenced], content.shift()));
1046     return this.processFences_(fences, content, split.head, contentStack);
1047   }
1048   // Final Case: A singular closing fence.
1049   // We turn the fence into a punctuation.
1050   var fenced = fences.shift();
1051   cvox.SemanticTree.fenceToPunct_(fenced);
1052   contentStack.push(contentStack.pop().concat([fenced], content.shift()));
1053   return this.processFences_(fences, content, openStack, contentStack);
1057 // TODO (sorge) The following could be done with linear programming.
1059  * Trys to combine neutral fences as much as possible.
1060  * @param {!Array<!cvox.SemanticTree.Node>} fences A list of neutral fences.
1061  * @param {!Array<!Array<cvox.SemanticTree.Node>>} content Intermediate
1062  *     content. Observe that |content| = |fences| - 1
1063  * @return {!Array<cvox.SemanticTree.Node>} List of node with fully fenced
1064  *     nodes.
1065  * @private
1066  */
1067 cvox.SemanticTree.prototype.processNeutralFences_ = function(fences, content) {
1068   if (fences.length == 0) {
1069     return fences;
1070   }
1071   if (fences.length == 1) {
1072     cvox.SemanticTree.fenceToPunct_(fences[0]);
1073     return fences;
1074     }
1075   var firstFence = fences.shift();
1076   var split = cvox.SemanticTree.sliceNodes_(
1077       fences, function(x) {return x.textContent == firstFence.textContent;});
1078   if (!split.div) {
1079     cvox.SemanticTree.fenceToPunct_(firstFence);
1080     var restContent = content.shift();
1081     restContent.unshift(firstFence);
1082     return restContent.concat(this.processNeutralFences_(fences, content));
1083   }
1084   var newContent = this.combineFencedContent_(
1085       firstFence, split.div, split.head, content);
1086   if (split.tail.length > 0) {
1087     var leftContent = newContent.shift();
1088     var result = this.processNeutralFences_(split.tail, newContent);
1089     return leftContent.concat(result);
1090   }
1091   return newContent[0];
1096  * Combines nodes framed by two matching fences using the given content.
1097  * Example: leftFence: [, rightFence: ], midFences: |, |
1098  *          content: c1, c2, c3, c4, ... cn
1099  *          return: [c1 | c2 | c3 ], c4, ... cn
1100  * @param {!cvox.SemanticTree.Node} leftFence The left fence.
1101  * @param {!cvox.SemanticTree.Node} rightFence The right fence.
1102  * @param {!Array<cvox.SemanticTree.Node>} midFences A list of intermediate
1103  *     fences.
1104  * @param {!Array<!Array<cvox.SemanticTree.Node>>} content Intermediate
1105  *     content. Observe that |content| = |fences| - 1 + k where k >= 0 is the
1106  *     remainder.
1107  * @return {!Array<!Array<cvox.SemanticTree.Node>>} List of content nodes
1108  *     where the first is the fully fenced node wrt. the given left and right
1109  *     fence.
1110  * @private
1111  */
1112 cvox.SemanticTree.prototype.combineFencedContent_ = function(
1113     leftFence, rightFence, midFences, content) {
1115   if (midFences.length == 0) {
1116     var fenced = this.makeHorizontalFencedNode_(
1117         leftFence, rightFence, content.shift());
1118     content.unshift(fenced);
1119     return content;
1120   }
1122   var leftContent = content.shift();
1123   var cutLength = midFences.length - 1;
1124   var midContent = content.slice(0, cutLength);
1125   content = content.slice(cutLength);
1126   var rightContent = content.shift();
1127   var innerNodes = this.processNeutralFences_(midFences, midContent);
1128   leftContent.push.apply(leftContent, innerNodes);
1129   leftContent.push.apply(leftContent, rightContent);
1130   var fenced = this.makeHorizontalFencedNode_(
1131       leftFence, rightFence, leftContent);
1132   if (content.length > 0) {
1133     content[0].unshift(fenced);
1134   } else {
1135     content = [[fenced]];
1136   }
1137   return content;
1138  };
1142  * Rewrite fences into punctuation. This is done with any "leftover" fence.
1143  * @param {cvox.SemanticTree.Node} fence Fence.
1144  * @private
1145  */
1146 cvox.SemanticTree.fenceToPunct_ = function(fence) {
1147   fence.type = cvox.SemanticAttr.Type.PUNCTUATION;
1148   switch (fence.role) {
1149     case cvox.SemanticAttr.Role.NEUTRAL:
1150     fence.role = cvox.SemanticAttr.Role.VBAR;
1151     break;
1152     case cvox.SemanticAttr.Role.OPEN:
1153     fence.role = cvox.SemanticAttr.Role.OPENFENCE;
1154     break;
1155     case cvox.SemanticAttr.Role.CLOSE:
1156     fence.role = cvox.SemanticAttr.Role.CLOSEFENCE;
1157     break;
1158   }
1163  * Create a fenced node.
1164  * @param {cvox.SemanticTree.Node} ofence Opening fence.
1165  * @param {cvox.SemanticTree.Node} cfence Closing fence.
1166  * @param {!Array<cvox.SemanticTree.Node>} content The content
1167  *     between the fences.
1168  * @return {!cvox.SemanticTree.Node} The new node.
1169  * @private
1170  */
1171 cvox.SemanticTree.prototype.makeHorizontalFencedNode_ = function(
1172     ofence, cfence, content) {
1173   var childNode = this.processRow_(content);
1174   var newNode = this.makeBranchNode_(
1175       cvox.SemanticAttr.Type.FENCED, [childNode], [ofence, cfence]);
1176   if (ofence.role == cvox.SemanticAttr.Role.OPEN) {
1177     newNode.role = cvox.SemanticAttr.Role.LEFTRIGHT;
1178   } else {
1179     newNode.role = ofence.role;
1180   }
1181   return newNode;
1186  * Combines sequences of punctuated expressions in a list of nodes.
1187  * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes.
1188  * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes.
1189  * @private
1190  */
1191 cvox.SemanticTree.prototype.getPunctuationInRow_ = function(nodes) {
1192   // For now we just make a punctuation node with a particular role. This is
1193   // similar to an mrow. The only exception are ellipses, which we assume to be
1194   // in lieu of identifiers.
1195   // In addition we keep the single punctuation nodes as content.
1196   var partition = cvox.SemanticTree.partitionNodes_(
1197       nodes, function(x) {
1198         return cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(x) &&
1199             !cvox.SemanticTree.attrPred_('role', 'ELLIPSIS')(x);});
1200   if (partition.rel.length == 0) {
1201     return nodes;
1202   }
1203   var newNodes = [];
1204   var firstComp = partition.comp.shift();
1205   if (firstComp.length > 0) {
1206     newNodes.push(this.processRow_(firstComp));
1207   }
1208   var relCounter = 0;
1209   while (partition.comp.length > 0) {
1210     newNodes.push(partition.rel[relCounter++]);
1211     firstComp = partition.comp.shift();
1212     if (firstComp.length > 0) {
1213       newNodes.push(this.processRow_(firstComp));
1214     }
1215   }
1216   return [this.makePunctuatedNode_(newNodes, partition.rel)];
1221  * Create a punctuated node.
1222  * @param {!Array<!cvox.SemanticTree.Node>} nodes List of all nodes separated
1223  * by punctuations.
1224  * @param {!Array<!cvox.SemanticTree.Node>} punctuations List of all separating
1225  * punctations. Observe that punctations is a subset of nodes.
1226  * @return {!cvox.SemanticTree.Node}
1227  * @private
1228  */
1229 cvox.SemanticTree.prototype.makePunctuatedNode_ = function(
1230     nodes, punctuations) {
1231   var newNode = this.makeBranchNode_(
1232       cvox.SemanticAttr.Type.PUNCTUATED, nodes, punctuations);
1234   if (punctuations.length == 1 &&
1235       nodes[0].type == cvox.SemanticAttr.Type.PUNCTUATION) {
1236     newNode.role = cvox.SemanticAttr.Role.STARTPUNCT;
1237   } else if (punctuations.length == 1 &&
1238       nodes[nodes.length - 1].type == cvox.SemanticAttr.Type.PUNCTUATION) {
1239     newNode.role = cvox.SemanticAttr.Role.ENDPUNCT;
1240   } else {
1241     newNode.role = cvox.SemanticAttr.Role.SEQUENCE;
1242   }
1243   return newNode;
1248  * Creates a limit node from a sub/superscript or over/under node if the central
1249  * element is a big operator. Otherwise it creates the standard elements.
1250  * @param {string} mmlTag The tag name of the original node.
1251  * @param {!Array<!cvox.SemanticTree.Node>} children The children of the
1252  *     original node.
1253  * @return {!cvox.SemanticTree.Node} The newly created limit node.
1254  * @private
1255  */
1256 cvox.SemanticTree.prototype.makeLimitNode_ = function(mmlTag, children) {
1257   var center = children[0];
1258   var isFunction = cvox.SemanticTree.attrPred_('type', 'FUNCTION')(center);
1259   // TODO (sorge) Put this into a single function.
1260   var isLimit = cvox.SemanticTree.attrPred_('type', 'LARGEOP')(center) ||
1261       cvox.SemanticTree.attrPred_('type', 'LIMBOTH')(center) ||
1262       cvox.SemanticTree.attrPred_('type', 'LIMLOWER')(center) ||
1263       cvox.SemanticTree.attrPred_('type', 'LIMUPPER')(center) ||
1264       (isFunction && cvox.SemanticTree.attrPred_('role', 'LIMFUNC')(center));
1265   var type = cvox.SemanticAttr.Type.UNKNOWN;
1266   // TODO (sorge) Make use of the difference in information on sub vs under etc.
1267   if (isLimit) {
1268     switch (mmlTag) {
1269       case 'MSUB':
1270       case 'MUNDER':
1271       type = cvox.SemanticAttr.Type.LIMLOWER;
1272       break;
1273       case 'MSUP':
1274       case 'MOVER':
1275       type = cvox.SemanticAttr.Type.LIMUPPER;
1276       break;
1277       case 'MSUBSUP':
1278       case 'MUNDEROVER':
1279       type = cvox.SemanticAttr.Type.LIMBOTH;
1280       break;
1281     }
1282   } else {
1283     switch (mmlTag) {
1284       case 'MSUB':
1285       type = cvox.SemanticAttr.Type.SUBSCRIPT;
1286       break;
1287       case 'MSUP':
1288       type = cvox.SemanticAttr.Type.SUPERSCRIPT;
1289       break;
1290       case 'MSUBSUP':
1291       var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.SUBSCRIPT,
1292                                       [center, children[1]], []);
1293       innerNode.role = center.role;
1294       children = [innerNode, children[2]];
1295       type = cvox.SemanticAttr.Type.SUPERSCRIPT;
1296       break;
1297       case 'MOVER':
1298       type = cvox.SemanticAttr.Type.OVERSCORE;
1299       break;
1300       case 'MUNDER':
1301       type = cvox.SemanticAttr.Type.UNDERSCORE;
1302       break;
1303       case 'MUNDEROVER':
1304       default:
1305       var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.UNDERSCORE,
1306                                       [center, children[1]], []);
1307       innerNode.role = center.role;
1308       children = [innerNode, children[2]];
1309       type = cvox.SemanticAttr.Type.OVERSCORE;
1310       break;
1311     }
1312   }
1313   var newNode = this.makeBranchNode_(type, children, []);
1314   newNode.role = center.role;
1315   return newNode;
1320  * Recursive method to accumulate function expressions.
1322  * The idea is to process functions in a row from left to right combining them
1323  * with there arguments. Thereby we take the notion of a function rather broadly
1324  * as a functional expressions that consists of a prefix and some arguments.
1325  * In particular we distinguish four types of functional expressions:
1326  * - integral: Integral expression.
1327  * - bigop: A big operator expression like a sum.
1328  * - prefix: A well defined prefix function such as sin, cos or a limit
1329  *           functions like lim, max.
1330  * - simple: An expression consisting of letters that are potentially a function
1331  *           symbol. If we have an explicit function application symbol
1332  *           following the expression we turn into a prefix function. Otherwise
1333  *           we decide heuristically if we could have a function application.
1334  * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of
1335  *     nodes.
1336  * @param {!Array<cvox.SemanticTree.Node>=} result The result node list.
1337  * @return {!Array<!cvox.SemanticTree.Node>} The fully processed list.
1338  * @private
1339  */
1340 cvox.SemanticTree.prototype.getFunctionsInRow_ = function(restNodes, result) {
1341   result = result || [];
1342   // Base case.
1343   if (restNodes.length == 0) {
1344     return result;
1345   }
1346   var firstNode = /** @type {!cvox.SemanticTree.Node} */ (restNodes.shift());
1347   var heuristic = cvox.SemanticTree.classifyFunction_(firstNode, restNodes);
1348   // First node is not a function node.
1349   if (!heuristic) {
1350     result.push(firstNode);
1351     return this.getFunctionsInRow_(restNodes, result);
1352   }
1353   // Combine functions in the rest of the row.
1354   var processedRest = this.getFunctionsInRow_(restNodes, []);
1355   var newRest = this.getFunctionArgs_(firstNode, processedRest, heuristic);
1356   return result.concat(newRest);
1361  * Classifies a function wrt. the heuristic that should be applied.
1362  * @param {!cvox.SemanticTree.Node} funcNode The node to be classified.
1363  * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of
1364  *     nodes. They can useful to look ahead if there is an explicit function
1365  *     application. If there is one, it will be destructively removed!
1366  * @return {!string} The string specifying the heuristic.
1367  * @private
1368  */
1369 cvox.SemanticTree.classifyFunction_ = function(funcNode, restNodes) {
1370   //  We do not allow double function application. This is not lambda calculus!
1371   if (funcNode.type == cvox.SemanticAttr.Type.APPL ||
1372       funcNode.type == cvox.SemanticAttr.Type.BIGOP ||
1373           funcNode.type == cvox.SemanticAttr.Type.INTEGRAL) {
1374     return '';
1375   }
1376   // Find and remove explicit function applications.
1377   // We now treat funcNode as a prefix function, regardless of what its actual
1378   // content is.
1379   if (restNodes[0] &&
1380       restNodes[0].textContent == cvox.SemanticAttr.functionApplication()) {
1381     // Remove explicit function application. This is destructive on the
1382     // underlying list.
1383     restNodes.shift();
1384     cvox.SemanticTree.propagatePrefixFunc_(funcNode);
1385     return 'prefix';
1386   }
1387   switch (funcNode.role) {
1388     case cvox.SemanticAttr.Role.INTEGRAL:
1389     return 'integral';
1390     break;
1391     case cvox.SemanticAttr.Role.SUM:
1392     return 'bigop';
1393     break;
1394     case cvox.SemanticAttr.Role.PREFIXFUNC:
1395     case cvox.SemanticAttr.Role.LIMFUNC:
1396     return 'prefix';
1397     break;
1398     default:
1399     if (funcNode.type == cvox.SemanticAttr.Type.IDENTIFIER) {
1400       return 'simple';
1401     }
1402   }
1403   return '';
1408  * Propagates a prefix function role in a node.
1409  * @param {cvox.SemanticTree.Node} funcNode The node whose role is to be
1410  * rewritten.
1411  * @private
1412  */
1413 cvox.SemanticTree.propagatePrefixFunc_ = function(funcNode) {
1414   if (funcNode) {
1415     funcNode.role = cvox.SemanticAttr.Role.PREFIXFUNC;
1416     cvox.SemanticTree.propagatePrefixFunc_(funcNode.childNodes[0]);
1417   }
1422  * Computes the arguments for a function from a list of nodes depending on the
1423  * given heuristic.
1424  * @param {!cvox.SemanticTree.Node} func A function node.
1425  * @param {!Array<cvox.SemanticTree.Node>} rest List of nodes to choose
1426  *     arguments from.
1427  * @param {string} heuristic The heuristic to follow.
1428  * @return {!Array<!cvox.SemanticTree.Node>} The function and the remainder of
1429  *     the rest list.
1430  * @private
1431  */
1432 cvox.SemanticTree.prototype.getFunctionArgs_ = function(func, rest, heuristic) {
1433   switch (heuristic) {
1434     case 'integral':
1435     var components = this.getIntegralArgs_(rest);
1436     var integrand = this.processRow_(components.integrand);
1437     var funcNode = this.makeIntegralNode_(func, integrand, components.intvar);
1438     components.rest.unshift(funcNode);
1439     return components.rest;
1440     break;
1441     case 'prefix':
1442     if (rest[0] && rest[0].type == cvox.SemanticAttr.Type.FENCED) {
1443       funcNode = this.makeFunctionNode_(
1444           func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift()));
1445       rest.unshift(funcNode);
1446       return rest;
1447     }
1448     case 'bigop':
1449     var partition = cvox.SemanticTree.sliceNodes_(
1450         rest, cvox.SemanticTree.prefixFunctionBoundary_);
1451     var arg = this.processRow_(partition.head);
1452     if (heuristic == 'prefix') {
1453       funcNode = this.makeFunctionNode_(func, arg);
1454     } else {
1455       funcNode = this.makeBigOpNode_(func, arg);
1456     }
1457     if (partition.div) {
1458       partition.tail.unshift(partition.div);
1459     }
1460     partition.tail.unshift(funcNode);
1461     return partition.tail;
1462     break;
1463     case 'simple':
1464     if (rest.length == 0) {
1465       return [func];
1466     }
1467     var firstArg = rest[0];
1468     if (firstArg.type == cvox.SemanticAttr.Type.FENCED &&
1469         firstArg.role != cvox.SemanticAttr.Role.NEUTRAL &&
1470             this.simpleFunctionHeuristic_(firstArg)) {
1471       funcNode = this.makeFunctionNode_(
1472           func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift()));
1473       rest.unshift(funcNode);
1474       return rest;
1475     }
1476     rest.unshift(func);
1477     return rest;
1478     break;
1479   }
1484  * Tail recursive function to obtain integral arguments.
1485  * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to take
1486  * arguments from.
1487  * @param {Array<cvox.SemanticTree.Node>=} args List of integral arguments.
1488  * @return {{integrand: !Array<cvox.SemanticTree.Node>,
1489  *     intvar: cvox.SemanticTree.Node,
1490  *     rest: !Array<cvox.SemanticTree.Node>}}
1491  *     Result split into integrand, integral variable and the remaining
1492  *     elements.
1493  * @private
1494  */
1495 cvox.SemanticTree.prototype.getIntegralArgs_ = function(nodes, args) {
1496   args = args || [];
1497   if (nodes.length == 0) {
1498     return {integrand: args, intvar: null, rest: nodes};
1499   }
1500   var firstNode = nodes[0];
1501   if (cvox.SemanticTree.generalFunctionBoundary_(firstNode)) {
1502     return {integrand: args, intvar: null, rest: nodes};
1503   }
1504   if (cvox.SemanticTree.integralDxBoundarySingle_(firstNode)) {
1505     return {integrand: args, intvar: firstNode, rest: nodes.slice(1)};
1506   }
1507   if (nodes[1] && cvox.SemanticTree.integralDxBoundary_(firstNode, nodes[1])) {
1508     var comma = this.createNode_();
1509     comma.updateContent_(cvox.SemanticAttr.invisibleComma());
1510     var intvar = this.makePunctuatedNode_(
1511         [firstNode, comma, nodes[1]], [comma]);
1512     intvar.role = cvox.SemanticAttr.Role.INTEGRAL;
1513     return {integrand: args, intvar: intvar, rest: nodes.slice(2)};
1514   }
1515   args.push(nodes.shift());
1516   return this.getIntegralArgs_(nodes, args);
1521  * Create a function node.
1522  * @param {!cvox.SemanticTree.Node} func The function operator.
1523  * @param {!cvox.SemanticTree.Node} arg The argument.
1524  * @return {!cvox.SemanticTree.Node} The new function node.
1525  * @private
1526  */
1527 cvox.SemanticTree.prototype.makeFunctionNode_ = function(func, arg) {
1528   var applNode = this.createNode_();
1529   applNode.updateContent_(cvox.SemanticAttr.functionApplication());
1530   applNode.type = cvox.SemanticAttr.Type.PUNCTUATION;
1531   applNode.role = cvox.SemanticAttr.Role.APPLICATION;
1532   var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.APPL, [func, arg],
1533                                 [applNode]);
1534   newNode.role = func.role;
1535   return newNode;
1540  * Create a big operator node.
1541  * @param {!cvox.SemanticTree.Node} bigOp The big operator.
1542  * @param {!cvox.SemanticTree.Node} arg The argument.
1543  * @return {!cvox.SemanticTree.Node} The new big operator node.
1544  * @private
1545  */
1546 cvox.SemanticTree.prototype.makeBigOpNode_ = function(bigOp, arg) {
1547   var newNode = this.makeBranchNode_(
1548       cvox.SemanticAttr.Type.BIGOP, [bigOp, arg], []);
1549   newNode.role = bigOp.role;
1550   return newNode;
1555  * Create an integral node. It has three children: integral, integrand and
1556  * integration variable. The latter two can be omitted.
1557  * @param {!cvox.SemanticTree.Node} integral The integral operator.
1558  * @param {cvox.SemanticTree.Node} integrand The integrand.
1559  * @param {cvox.SemanticTree.Node} intvar The integral variable.
1560  * @return {!cvox.SemanticTree.Node} The new integral node.
1561  * @private
1562  */
1563 cvox.SemanticTree.prototype.makeIntegralNode_ = function(
1564     integral, integrand, intvar) {
1565   integrand = integrand || this.makeEmptyNode_();
1566   intvar = intvar || this.makeEmptyNode_();
1567   var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.INTEGRAL,
1568                                 [integral, integrand, intvar], []);
1569   newNode.role = integral.role;
1570   return newNode;
1575  * Predicate implementing the boundary criteria for simple functions:
1577  * @param {!cvox.SemanticTree.Node} node A semantic node of type fenced.
1578  * @return {boolean} True if the node meets the boundary criteria.
1579  * @private
1580  */
1581 cvox.SemanticTree.prototype.simpleFunctionHeuristic_ = function(node) {
1582   var children = node.childNodes;
1583   if (children.length == 0) {
1584     return true;
1585   }
1586   if (children.length > 1) {
1587     return false;
1588   }
1589   var child = children[0];
1590   if (child.type == cvox.SemanticAttr.Type.INFIXOP) {
1591     if (child.role != cvox.SemanticAttr.Role.IMPLICIT) {
1592       return false;
1593     }
1594     if (child.childNodes.some(cvox.SemanticTree.attrPred_('type', 'INFIXOP'))) {
1595       return false;
1596     }
1597   }
1598   return true;
1603  * Predicate implementing the boundary criteria for prefix functions and big
1604  * operators:
1605  * 1. an explicit operator,
1606  * 2. a relation symbol, or
1607  * 3. some punctuation.
1608  * @param {cvox.SemanticTree.Node} node A semantic node.
1609  * @return {boolean} True if the node meets the boundary criteria.
1610  * @private
1611  */
1612 cvox.SemanticTree.prefixFunctionBoundary_ = function(node) {
1613   return cvox.SemanticTree.attrPred_('type', 'OPERATOR')(node) ||
1614       cvox.SemanticTree.generalFunctionBoundary_(node);
1619  * Predicate implementing the boundary criteria for integrals dx on two nodes.
1620  * @param {cvox.SemanticTree.Node} firstNode A semantic node.
1621  * @param {cvox.SemanticTree.Node} secondNode The direct neighbour of first
1622  *     Node.
1623  * @return {boolean} True if the second node exists and the first node is a 'd'.
1624  * @private
1625  */
1626 cvox.SemanticTree.integralDxBoundary_ = function(
1627     firstNode, secondNode) {
1628   return !!secondNode &&
1629       cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(secondNode) &&
1630           cvox.SemanticAttr.isCharacterD(firstNode.textContent);
1635  * Predicate implementing the boundary criteria for integrals dx on a single
1636  * node.
1637  * @param {cvox.SemanticTree.Node} node A semantic node.
1638  * @return {boolean} True if the node meets the boundary criteria.
1639  * @private
1640  */
1641 cvox.SemanticTree.integralDxBoundarySingle_ = function(node) {
1642   if (cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(node)) {
1643     var firstChar = node.textContent[0];
1644     return firstChar && node.textContent[1] &&
1645         cvox.SemanticAttr.isCharacterD(firstChar);
1646   }
1647   return false;
1652  * Predicate implementing the general boundary criteria for function operators:
1653  * 1. a relation symbol,
1654  * 2. some punctuation.
1655  * @param {cvox.SemanticTree.Node} node A semantic node.
1656  * @return {boolean} True if the node meets the boundary criteria.
1657  * @private
1658  */
1659 cvox.SemanticTree.generalFunctionBoundary_ = function(node) {
1660   return cvox.SemanticTree.attrPred_('type', 'RELATION')(node) ||
1661       cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(node);
1666  * Rewrites tables into matrices or case statements in a list of nodes.
1667  * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to rewrite.
1668  * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes.
1669  * @private
1670  */
1671 cvox.SemanticTree.prototype.processTablesInRow_ = function(nodes) {
1672   // First we process all matrices:
1673   var partition = cvox.SemanticTree.partitionNodes_(
1674       nodes, cvox.SemanticTree.tableIsMatrixOrVector_);
1675   var result = [];
1676   for (var i = 0, matrix; matrix = partition.rel[i]; i++) {
1677     result = result.concat(partition.comp.shift());
1678     result.push(this.tableToMatrixOrVector_(matrix));
1679   }
1680   result = result.concat(partition.comp.shift());
1681   // Process the remaining tables for cases.
1682   partition = cvox.SemanticTree.partitionNodes_(
1683       result, cvox.SemanticTree.isTableOrMultiline_);
1684   result = [];
1685   for (var i = 0, table; table = partition.rel[i]; i++) {
1686     var prevNodes = partition.comp.shift();
1687     if (cvox.SemanticTree.tableIsCases_(table, prevNodes)) {
1688       this.tableToCases_(
1689           table, /** @type {!cvox.SemanticTree.Node} */ (prevNodes.pop()));
1690     }
1691     result = result.concat(prevNodes);
1692     result.push(table);
1693   }
1694   return result.concat(partition.comp.shift());
1699  * Decides if a node is a table or multiline element.
1700  * @param {cvox.SemanticTree.Node} node A node.
1701  * @return {boolean} True if node is either table or multiline.
1702  * @private
1703  */
1704 cvox.SemanticTree.isTableOrMultiline_ = function(node) {
1705   return !!node && (cvox.SemanticTree.attrPred_('type', 'TABLE')(node) ||
1706       cvox.SemanticTree.attrPred_('type', 'MULTILINE')(node));
1711  * Heuristic to decide if we have a matrix: An expression fenced on both sides
1712  * without any other content is considered a fenced node.
1713  * @param {cvox.SemanticTree.Node} node A node.
1714  * @return {boolean} True if we believe we have a matrix.
1715  * @private
1716  */
1717 cvox.SemanticTree.tableIsMatrixOrVector_ = function(node) {
1718   return !!node && cvox.SemanticTree.attrPred_('type', 'FENCED')(node) &&
1719       cvox.SemanticTree.attrPred_('role', 'LEFTRIGHT')(node) &&
1720           node.childNodes.length == 1 &&
1721               cvox.SemanticTree.isTableOrMultiline_(node.childNodes[0]);
1726  * Replaces a fenced node by a matrix or vector node.
1727  * @param {!cvox.SemanticTree.Node} node The fenced node to be rewritten.
1728  * @return {!cvox.SemanticTree.Node} The matrix or vector node.
1729  * @private
1730  */
1731 cvox.SemanticTree.prototype.tableToMatrixOrVector_ = function(node) {
1732   var matrix = node.childNodes[0];
1733   var type = cvox.SemanticTree.attrPred_('type', 'MULTILINE')(matrix) ?
1734       'VECTOR' : 'MATRIX';
1735   matrix.type = cvox.SemanticAttr.Type[type];
1736   node.contentNodes.forEach(goog.bind(matrix.appendContentNode_, matrix));
1737   for (var i = 0, row; row = matrix.childNodes[i]; i++) {
1738     cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role[type]);
1739   }
1740   return matrix;
1745  * Heuristic to decide if we have a case statement: An expression with a
1746  * singular open fence before it.
1747  * @param {!cvox.SemanticTree.Node} table A table node.
1748  * @param {!Array<cvox.SemanticTree.Node>} prevNodes A list of previous nodes.
1749  * @return {boolean} True if we believe we have a case statement.
1750  * @private
1751  */
1752 cvox.SemanticTree.tableIsCases_ = function(table, prevNodes) {
1753   return prevNodes.length > 0 &&
1754       cvox.SemanticTree.attrPred_('role', 'OPENFENCE')(
1755           prevNodes[prevNodes.length - 1]);
1760  * Makes case node out of a table and a fence.
1761  * @param {!cvox.SemanticTree.Node} table The table containing the cases.
1762  * @param {!cvox.SemanticTree.Node} openFence The left delimiter of the case
1763  *     statement.
1764  * @return {!cvox.SemanticTree.Node} The cases node.
1765  * @private
1766  */
1767 cvox.SemanticTree.prototype.tableToCases_ = function(table, openFence) {
1768   for (var i = 0, row; row = table.childNodes[i]; i++) {
1769     cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role.CASES);
1770     // }
1771   }
1772   table.type = cvox.SemanticAttr.Type.CASES;
1773   table.appendContentNode_(openFence);
1774   return table;
1778 // TODO (sorge) This heuristic is very primitive. We could start reworking
1779 // multilines, by combining all cells, semantically rewriting the entire line
1780 // and see if there are any similarities. Alternatively, we could look for
1781 // similarities in columns (e.g., single relation symbols, like equalities or
1782 // inequalities in the same column could indicate an equation array).
1784  * Heuristic to decide if we have a multiline formula. A table is considered a
1785  * multiline formula if it does not have any separate cells.
1786  * @param {!cvox.SemanticTree.Node} table A table node.
1787  * @return {boolean} True if we believe we have a mulitline formula.
1788  * @private
1789  */
1790 cvox.SemanticTree.tableIsMultiline_ = function(table) {
1791   return table.childNodes.every(
1792       function(row) {
1793         var length = row.childNodes.length;
1794         return length <= 1;});
1799  * Rewrites a table to multiline structure, simplifying it by getting rid of the
1800  * cell hierarchy level.
1801  * @param {!cvox.SemanticTree.Node} table The node to be rewritten a multiline.
1802  * @private
1803  */
1804 cvox.SemanticTree.prototype.tableToMultiline_ = function(table) {
1805   table.type = cvox.SemanticAttr.Type.MULTILINE;
1806   for (var i = 0, row; row = table.childNodes[i]; i++) {
1807     cvox.SemanticTree.rowToLine_(row, cvox.SemanticAttr.Role.MULTILINE);
1808   }
1813  * Converts a row that only contains one cell into a single line.
1814  * @param {!cvox.SemanticTree.Node} row The row to convert.
1815  * @param {cvox.SemanticAttr.Role=} role The new role for the line.
1816  * @private
1817  */
1818 cvox.SemanticTree.rowToLine_ = function(row, role) {
1819   role = role || cvox.SemanticAttr.Role.UNKNOWN;
1820   if (cvox.SemanticTree.attrPred_('type', 'ROW')(row) &&
1821       row.childNodes.length == 1 &&
1822           cvox.SemanticTree.attrPred_('type', 'CELL')(row.childNodes[0])) {
1823     row.type = cvox.SemanticAttr.Type.LINE;
1824     row.role = role;
1825     row.childNodes = row.childNodes[0].childNodes;
1826   }
1831  * Assign a row and its contained cells a new role value.
1832  * @param {!cvox.SemanticTree.Node} row The row to be updated.
1833  * @param {!cvox.SemanticAttr.Role} role The new role for the row and its cells.
1834  * @private
1835  */
1836 cvox.SemanticTree.assignRoleToRow_ = function(row, role) {
1837   if (cvox.SemanticTree.attrPred_('type', 'LINE')(row)) {
1838     row.role = role;
1839     return;
1840   }
1841   if (cvox.SemanticTree.attrPred_('type', 'ROW')(row)) {
1842     row.role = role;
1843     var cellPred = cvox.SemanticTree.attrPred_('type', 'CELL');
1844     row.childNodes.forEach(function(cell) {
1845       if (cellPred(cell)) {
1846         cell.role = role;
1847       }
1848     });
1849   }
1854  * Splits a list of nodes wrt. to a given predicate.
1855  * @param {Array<cvox.SemanticTree.Node>} nodes A list of nodes.
1856  * @param {!function(cvox.SemanticTree.Node): boolean} pred Predicate for the
1857  *    partitioning relation.
1858  * @param {boolean=} reverse If true slicing is done from the end.
1859  * @return {{head: !Array<cvox.SemanticTree.Node>,
1860  *           div: cvox.SemanticTree.Node,
1861  *           tail: !Array<cvox.SemanticTree.Node>}} The split list.
1862  * @private
1863  */
1864 cvox.SemanticTree.sliceNodes_ = function(nodes, pred, reverse) {
1865   if (reverse) {
1866     nodes.reverse();
1867   }
1868   var head = [];
1869   for (var i = 0, node; node = nodes[i]; i++) {
1870     if (pred(node)) {
1871       if (reverse) {
1872         return {head: nodes.slice(i + 1).reverse(),
1873                 div: node,
1874                 tail: head.reverse()};
1875       }
1876       return {head: head,
1877               div: node,
1878               tail: nodes.slice(i + 1)};
1879     }
1880     head.push(node);
1881   }
1882   if (reverse) {
1883     return {head: [], div: null, tail: head.reverse()};
1884   }
1885   return {head: head, div: null, tail: []};
1890  * Partitions a list of nodes wrt. to a given predicate. Effectively works like
1891  * a PER on the ordered set of nodes.
1892  * @param {!Array<!cvox.SemanticTree.Node>} nodes A list of nodes.
1893  * @param {!function(cvox.SemanticTree.Node): boolean} pred Predicate for the
1894  *    partitioning relation.
1895  * @return {{rel: !Array<cvox.SemanticTree.Node>,
1896  *           comp: !Array<!Array<cvox.SemanticTree.Node>>}}
1897  *    The partitioning given in terms of a collection of elements satisfying
1898  *    the predicate and a collection of complementary sets lying inbetween the
1899  *    related elements. Observe that we always have |comp| = |rel| + 1.
1901  * Example: On input [a, r_1, b, c, r_2, d, e, r_3] where P(r_i) holds, we
1902  *    get as output: {rel: [r_1, r_2, r_3], comp: [[a], [b, c], [d, e], []].
1903  * @private
1904  */
1905 cvox.SemanticTree.partitionNodes_ = function(nodes, pred) {
1906   var restNodes = nodes;
1907   var rel = [];
1908   var comp = [];
1910   do {
1911     var result = cvox.SemanticTree.sliceNodes_(restNodes, pred);
1912     comp.push(result.head);
1913     rel.push(result.div);
1914     restNodes = result.tail;
1915   } while (result.div);
1916   rel.pop();
1917   return {rel: rel, comp: comp};
1922  * Constructs a predicate to check the semantic attribute of a node.
1923  * @param {!string} prop The property of a node.
1924  * @param {!string} attr The attribute.
1925  * @return {function(cvox.SemanticTree.Node): boolean} The predicate.
1926  * @private
1927  */
1929 cvox.SemanticTree.attrPred_ = function(prop, attr) {
1930   var getAttr = function(prop) {
1931     switch (prop) {
1932       case 'type': return cvox.SemanticAttr.Type[attr];
1933       case 'role': return cvox.SemanticAttr.Role[attr];
1934       case 'font': return cvox.SemanticAttr.Font[attr];
1935     }
1936   };
1938   return function(node) {return node[prop] == getAttr(prop);};