Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(613)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree.js

Issue 2939273002: DO NOT SUBMIT: what chrome/browser/resources/ could eventually look like with clang-format (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview A semantic tree for MathML expressions. 6 * @fileoverview A semantic tree for MathML expressions.
7 * 7 *
8 * This file contains functionality to compute a semantic interpretation from a 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 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 10 * fairly simple default semantic which is suitable for K-12 and simple UG
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 for (var i = 0, child; child = this.childNodes[i]; i++) { 91 for (var i = 0, child; child = this.childNodes[i]; i++) {
92 result = result.concat(child.querySelectorAll(pred)); 92 result = result.concat(child.querySelectorAll(pred));
93 } 93 }
94 if (pred(this)) { 94 if (pred(this)) {
95 result.unshift(this); 95 result.unshift(this);
96 } 96 }
97 return result; 97 return result;
98 }; 98 };
99 99
100 100
101 /** 101 /**
102 * Returns an XML representation of the tree. 102 * Returns an XML representation of the tree.
103 * @param {boolean=} brief If set attributes are omitted. 103 * @param {boolean=} brief If set attributes are omitted.
104 * @return {Node} The XML representation of the tree. 104 * @return {Node} The XML representation of the tree.
105 */ 105 */
106 cvox.SemanticTree.prototype.xml = function(brief) { 106 cvox.SemanticTree.prototype.xml = function(brief) {
107 var dp = new DOMParser(); 107 var dp = new DOMParser();
108 var xml = dp.parseFromString('<stree></stree>', 'text/xml'); 108 var xml = dp.parseFromString('<stree></stree>', 'text/xml');
109 109
110 var xmlRoot = this.root.xml(xml, brief); 110 var xmlRoot = this.root.xml(xml, brief);
111 xml.childNodes[0].appendChild(xmlRoot); 111 xml.childNodes[0].appendChild(xmlRoot);
112 112
113 return xml.childNodes[0]; 113 return xml.childNodes[0];
114 }; 114 };
115
116
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 };
151 115
152 116
153 /** 117 /**
154 * Serializes the XML representation of the tree. 118 * An XML tree representation of the current node.
155 * @param {boolean=} brief If set attributes are omitted. 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) {
132 return x.xml(xml, brief);
133 });
134 var tagNode = xml.createElement(tag);
135 for (var i = 0, child; child = xmlNodes[i]; i++) {
136 tagNode.appendChild(child);
137 }
138 return tagNode;
139 };
140 var node = xml.createElement(this.type);
141 if (!brief) {
142 this.xmlAttributes_(node);
143 }
144 node.textContent = this.textContent;
145 if (this.contentNodes.length > 0) {
146 node.appendChild(xmlNodeList('content', this.contentNodes));
147 }
148 if (this.childNodes.length > 0) {
149 node.appendChild(xmlNodeList('children', this.childNodes));
150 }
151 return node;
152 };
153
154
155 /**
156 * Serializes the XML representation of the tree.
157 * @param {boolean=} brief If set attributes are omitted.
156 * @return {string} Serialized string. 158 * @return {string} Serialized string.
157 */ 159 */
158 cvox.SemanticTree.prototype.toString = function(brief) { 160 cvox.SemanticTree.prototype.toString = function(brief) {
159 var xmls = new XMLSerializer(); 161 var xmls = new XMLSerializer();
160 return xmls.serializeToString(this.xml(brief)); 162 return xmls.serializeToString(this.xml(brief));
161 }; 163 };
162 164
163 165
164 /** 166 /**
165 * Pretty print the XML representation of the tree. 167 * Pretty print the XML representation of the tree.
(...skipping 11 matching lines...) Expand all
177 * @param {string} xml The serialised XML string. 179 * @param {string} xml The serialised XML string.
178 * @return {string} The formatted string. 180 * @return {string} The formatted string.
179 */ 181 */
180 cvox.SemanticTree.formatXml = function(xml) { 182 cvox.SemanticTree.formatXml = function(xml) {
181 var reg = /(>)(<)(\/*)/g; 183 var reg = /(>)(<)(\/*)/g;
182 xml = xml.replace(reg, '$1\r\n$2$3'); 184 xml = xml.replace(reg, '$1\r\n$2$3');
183 reg = /(>)(.+)(<c)/g; 185 reg = /(>)(.+)(<c)/g;
184 xml = xml.replace(reg, '$1\r\n$2\r\n$3'); 186 xml = xml.replace(reg, '$1\r\n$2\r\n$3');
185 var formatted = ''; 187 var formatted = '';
186 var padding = ''; 188 var padding = '';
187 xml.split('\r\n') 189 xml.split('\r\n').forEach(function(node) {
188 .forEach(function(node) { 190 if (node.match(/.+<\/\w[^>]*>$/)) {
189 if (node.match(/.+<\/\w[^>]*>$/)) { 191 // Node with content.
190 // Node with content. 192 formatted += padding + node + '\r\n';
191 formatted += padding + node + '\r\n'; 193 } else if (node.match(/^<\/\w/)) {
192 } else if (node.match(/^<\/\w/)) { 194 if (padding) {
193 if (padding) { 195 // Closing tag
194 // Closing tag 196 padding = padding.slice(2);
195 padding = padding.slice(2); 197 formatted += padding + node + '\r\n';
196 formatted += padding + node + '\r\n'; 198 }
197 } 199 } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
198 } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { 200 // Opening tag
199 // Opening tag 201 formatted += padding + node + '\r\n';
200 formatted += padding + node + '\r\n'; 202 padding += ' ';
201 padding += ' '; 203 } else {
202 } else { 204 // Empty tag
203 // Empty tag 205 formatted += padding + node + '\r\n';
204 formatted += padding + node + '\r\n'; 206 }
205 } 207 });
206 });
207 return formatted; 208 return formatted;
208 }; 209 };
209 210
210 211
211 /** 212 /**
212 * Serializes the XML representation of a node. 213 * Serializes the XML representation of a node.
213 * @param {boolean=} brief If attributes are to be omitted. 214 * @param {boolean=} brief If attributes are to be omitted.
214 * @return {string} Serialized string. 215 * @return {string} Serialized string.
215 */ 216 */
216 cvox.SemanticTree.Node.prototype.toString = function(brief) { 217 cvox.SemanticTree.Node.prototype.toString = function(brief) {
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
334 var index = this.childNodes.indexOf(oldNode); 335 var index = this.childNodes.indexOf(oldNode);
335 if (index == -1) { 336 if (index == -1) {
336 return; 337 return;
337 } 338 }
338 newNode.parent = this; 339 newNode.parent = this;
339 oldNode.parent = null; 340 oldNode.parent = null;
340 this.childNodes[index] = newNode; 341 this.childNodes[index] = newNode;
341 // To not mess up the order of MathML elements more than necessary, we only 342 // 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 // remove and add difference lists. The hope is that we might end up with
343 // little change. 344 // little change.
344 var removeMathml = oldNode.mathml.filter( 345 var removeMathml = oldNode.mathml.filter(function(x) {
345 function(x) {return newNode.mathml.indexOf(x) == -1;}); 346 return newNode.mathml.indexOf(x) == -1;
346 var addMathml = newNode.mathml.filter( 347 });
347 function(x) {return oldNode.mathml.indexOf(x) == -1;}); 348 var addMathml = newNode.mathml.filter(function(x) {
349 return oldNode.mathml.indexOf(x) == -1;
350 });
348 this.removeMathmlNodes_(removeMathml); 351 this.removeMathmlNodes_(removeMathml);
349 this.addMathmlNodes_(addMathml); 352 this.addMathmlNodes_(addMathml);
350 }; 353 };
351 354
352 355
353 /** 356 /**
354 * Appends a content node to the node. 357 * Appends a content node to the node.
355 * @param {cvox.SemanticTree.Node} node The new content node. 358 * @param {cvox.SemanticTree.Node} node The new content node.
356 * @private 359 * @private
357 */ 360 */
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
389 cvox.SemanticTree.prototype.parseMathml_ = function(mml) { 392 cvox.SemanticTree.prototype.parseMathml_ = function(mml) {
390 var children = cvox.DomUtil.toArray(mml.children); 393 var children = cvox.DomUtil.toArray(mml.children);
391 switch (cvox.SemanticUtil.tagName(mml)) { 394 switch (cvox.SemanticUtil.tagName(mml)) {
392 case 'MATH': 395 case 'MATH':
393 case 'MROW': 396 case 'MROW':
394 case 'MPADDED': 397 case 'MPADDED':
395 case 'MSTYLE': 398 case 'MSTYLE':
396 children = cvox.SemanticUtil.purgeNodes(children); 399 children = cvox.SemanticUtil.purgeNodes(children);
397 // Single child node, i.e. the row is meaningless. 400 // Single child node, i.e. the row is meaningless.
398 if (children.length == 1) { 401 if (children.length == 1) {
399 return this.parseMathml_(/** @type {!Element} */(children[0])); 402 return this.parseMathml_(/** @type {!Element} */ (children[0]));
400 } 403 }
401 // Case of a 'meaningful' row, even if they are empty. 404 // Case of a 'meaningful' row, even if they are empty.
402 return this.processRow_(this.parseMathmlChildren_(children)); 405 return this.processRow_(this.parseMathmlChildren_(children));
403 break; 406 break;
404 case 'MFRAC': 407 case 'MFRAC':
405 var newNode = this.makeBranchNode_( 408 var newNode = this.makeBranchNode_(
406 cvox.SemanticAttr.Type.FRACTION, 409 cvox.SemanticAttr.Type.FRACTION,
407 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], 410 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], []);
408 []);
409 newNode.role = cvox.SemanticAttr.Role.DIVISION; 411 newNode.role = cvox.SemanticAttr.Role.DIVISION;
410 return newNode; 412 return newNode;
411 break; 413 break;
412 case 'MSUB': 414 case 'MSUB':
413 case 'MSUP': 415 case 'MSUP':
414 case 'MSUBSUP': 416 case 'MSUBSUP':
415 case 'MOVER': 417 case 'MOVER':
416 case 'MUNDER': 418 case 'MUNDER':
417 case 'MUNDEROVER': 419 case 'MUNDEROVER':
418 return this.makeLimitNode_(cvox.SemanticUtil.tagName(mml), 420 return this.makeLimitNode_(
419 this.parseMathmlChildren_(children)); 421 cvox.SemanticUtil.tagName(mml), this.parseMathmlChildren_(children));
420 break; 422 break;
421 case 'MROOT': 423 case 'MROOT':
422 return this.makeBranchNode_( 424 return this.makeBranchNode_(
423 cvox.SemanticAttr.Type.ROOT, 425 cvox.SemanticAttr.Type.ROOT,
424 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], 426 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], []);
425 []);
426 break; 427 break;
427 case 'MSQRT': 428 case 'MSQRT':
428 children = this.parseMathmlChildren_( 429 children =
429 cvox.SemanticUtil.purgeNodes(children)); 430 this.parseMathmlChildren_(cvox.SemanticUtil.purgeNodes(children));
430 return this.makeBranchNode_( 431 return this.makeBranchNode_(
431 cvox.SemanticAttr.Type.SQRT, [this.processRow_(children)], []); 432 cvox.SemanticAttr.Type.SQRT, [this.processRow_(children)], []);
432 break; 433 break;
433 case 'MTABLE': 434 case 'MTABLE':
434 newNode = this.makeBranchNode_( 435 newNode = this.makeBranchNode_(
435 cvox.SemanticAttr.Type.TABLE, 436 cvox.SemanticAttr.Type.TABLE, this.parseMathmlChildren_(children),
436 this.parseMathmlChildren_(children), []); 437 []);
437 if (cvox.SemanticTree.tableIsMultiline_(newNode)) { 438 if (cvox.SemanticTree.tableIsMultiline_(newNode)) {
438 this.tableToMultiline_(newNode); 439 this.tableToMultiline_(newNode);
439 } 440 }
440 return newNode; 441 return newNode;
441 break; 442 break;
442 case 'MTR': 443 case 'MTR':
443 newNode = this.makeBranchNode_( 444 newNode = this.makeBranchNode_(
444 cvox.SemanticAttr.Type.ROW, 445 cvox.SemanticAttr.Type.ROW, this.parseMathmlChildren_(children), []);
445 this.parseMathmlChildren_(children), []);
446 newNode.role = cvox.SemanticAttr.Role.TABLE; 446 newNode.role = cvox.SemanticAttr.Role.TABLE;
447 return newNode; 447 return newNode;
448 break; 448 break;
449 case 'MTD': 449 case 'MTD':
450 children = this.parseMathmlChildren_( 450 children =
451 cvox.SemanticUtil.purgeNodes(children)); 451 this.parseMathmlChildren_(cvox.SemanticUtil.purgeNodes(children));
452 newNode = this.makeBranchNode_( 452 newNode = this.makeBranchNode_(
453 cvox.SemanticAttr.Type.CELL, [this.processRow_(children)], []); 453 cvox.SemanticAttr.Type.CELL, [this.processRow_(children)], []);
454 newNode.role = cvox.SemanticAttr.Role.TABLE; 454 newNode.role = cvox.SemanticAttr.Role.TABLE;
455 return newNode; 455 return newNode;
456 break; 456 break;
457 case 'MTEXT': 457 case 'MTEXT':
458 var leaf = this.makeLeafNode_(mml); 458 var leaf = this.makeLeafNode_(mml);
459 leaf.type = cvox.SemanticAttr.Type.TEXT; 459 leaf.type = cvox.SemanticAttr.Type.TEXT;
460 return leaf; 460 return leaf;
461 break; 461 break;
(...skipping 16 matching lines...) Expand all
478 break; 478 break;
479 case 'MO': 479 case 'MO':
480 leaf = this.makeLeafNode_(mml); 480 leaf = this.makeLeafNode_(mml);
481 if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) { 481 if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) {
482 leaf.type = cvox.SemanticAttr.Type.OPERATOR; 482 leaf.type = cvox.SemanticAttr.Type.OPERATOR;
483 } 483 }
484 return leaf; 484 return leaf;
485 break; 485 break;
486 // TODO (sorge) Do something useful with error and phantom symbols. 486 // TODO (sorge) Do something useful with error and phantom symbols.
487 default: 487 default:
488 // Ordinarilly at this point we should not get any other tag. 488 // Ordinarilly at this point we should not get any other tag.
489 return this.makeUnprocessed_(mml); 489 return this.makeUnprocessed_(mml);
490 } 490 }
491 }; 491 };
492 492
493 493
494 /** 494 /**
495 * Parse a list of MathML nodes into the semantic tree. 495 * Parse a list of MathML nodes into the semantic tree.
496 * @param {Array<Element>} mmls A list of MathML nodes. 496 * @param {Array<Element>} mmls A list of MathML nodes.
497 * @return {!Array<cvox.SemanticTree.Node>} The list of resulting semantic 497 * @return {!Array<cvox.SemanticTree.Node>} The list of resulting semantic
498 * node. 498 * node.
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
557 */ 557 */
558 cvox.SemanticTree.prototype.makeBranchNode_ = function( 558 cvox.SemanticTree.prototype.makeBranchNode_ = function(
559 type, children, contentNodes, content) { 559 type, children, contentNodes, content) {
560 var node = this.createNode_(); 560 var node = this.createNode_();
561 if (content) { 561 if (content) {
562 node.updateContent_(content); 562 node.updateContent_(content);
563 } 563 }
564 node.type = type; 564 node.type = type;
565 node.childNodes = children; 565 node.childNodes = children;
566 node.contentNodes = contentNodes; 566 node.contentNodes = contentNodes;
567 children.concat(contentNodes) 567 children.concat(contentNodes).forEach(function(x) {
568 .forEach( 568 x.parent = node;
569 function(x) { 569 node.addMathmlNodes_(x.mathml);
570 x.parent = node; 570 });
571 node.addMathmlNodes_(x.mathml);
572 });
573 return node; 571 return node;
574 }; 572 };
575 573
576 574
577 /** 575 /**
578 * Create a branching node for an implicit operation, currently assumed to 576 * Create a branching node for an implicit operation, currently assumed to
579 * be of multiplicative type. 577 * be of multiplicative type.
580 * @param {!Array<!cvox.SemanticTree.Node>} nodes The operands. 578 * @param {!Array<!cvox.SemanticTree.Node>} nodes The operands.
581 * @return {!cvox.SemanticTree.Node} The new branch node. 579 * @return {!cvox.SemanticTree.Node} The new branch node.
582 * @private 580 * @private
583 */ 581 */
584 cvox.SemanticTree.prototype.makeImplicitNode_ = function(nodes) { 582 cvox.SemanticTree.prototype.makeImplicitNode_ = function(nodes) {
585 if (nodes.length == 1) { 583 if (nodes.length == 1) {
586 return nodes[0]; 584 return nodes[0];
587 } 585 }
588 var operator = this.createNode_(); 586 var operator = this.createNode_();
589 // For now we assume this is a multiplication using invisible times. 587 // For now we assume this is a multiplication using invisible times.
590 operator.updateContent_(cvox.SemanticAttr.invisibleTimes()); 588 operator.updateContent_(cvox.SemanticAttr.invisibleTimes());
591 var newNode = this.makeInfixNode_(nodes, operator); 589 var newNode = this.makeInfixNode_(nodes, operator);
592 newNode.role = cvox.SemanticAttr.Role.IMPLICIT; 590 newNode.role = cvox.SemanticAttr.Role.IMPLICIT;
593 return newNode; 591 return newNode;
594 }; 592 };
595 593
596 594
597 /** 595 /**
(...skipping 16 matching lines...) Expand all
614 * @param {!cvox.SemanticTree.Node} inner The inner node. 612 * @param {!cvox.SemanticTree.Node} inner The inner node.
615 * @param {!Array<cvox.SemanticTree.Node>} nodeList List of nodes. 613 * @param {!Array<cvox.SemanticTree.Node>} nodeList List of nodes.
616 * @param {!cvox.SemanticAttr.Type} type The new type of the node. 614 * @param {!cvox.SemanticAttr.Type} type The new type of the node.
617 * @return {!cvox.SemanticTree.Node} The new branch node. 615 * @return {!cvox.SemanticTree.Node} The new branch node.
618 * @private 616 * @private
619 */ 617 */
620 cvox.SemanticTree.prototype.makeConcatNode_ = function(inner, nodeList, type) { 618 cvox.SemanticTree.prototype.makeConcatNode_ = function(inner, nodeList, type) {
621 if (nodeList.length == 0) { 619 if (nodeList.length == 0) {
622 return inner; 620 return inner;
623 } 621 }
624 var content = nodeList.map(function(x) {return x.textContent;}).join(' '); 622 var content = nodeList
623 .map(function(x) {
624 return x.textContent;
625 })
626 .join(' ');
625 var newNode = this.makeBranchNode_(type, [inner], nodeList, content); 627 var newNode = this.makeBranchNode_(type, [inner], nodeList, content);
626 if (nodeList.length > 0) { 628 if (nodeList.length > 0) {
627 newNode.role = cvox.SemanticAttr.Role.MULTIOP; 629 newNode.role = cvox.SemanticAttr.Role.MULTIOP;
628 } 630 }
629 return newNode; 631 return newNode;
630 }; 632 };
631 633
632 634
633 /** 635 /**
634 * Wraps a node into prefix operators. 636 * Wraps a node into prefix operators.
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
710 var partition = cvox.SemanticTree.partitionNodes_( 712 var partition = cvox.SemanticTree.partitionNodes_(
711 nodes, cvox.SemanticTree.attrPred_('type', 'RELATION')); 713 nodes, cvox.SemanticTree.attrPred_('type', 'RELATION'));
712 var firstRel = partition.rel[0]; 714 var firstRel = partition.rel[0];
713 715
714 if (!firstRel) { 716 if (!firstRel) {
715 return this.processOperationsInRow_(nodes); 717 return this.processOperationsInRow_(nodes);
716 } 718 }
717 if (nodes.length == 1) { 719 if (nodes.length == 1) {
718 return nodes[0]; 720 return nodes[0];
719 } 721 }
720 var children = partition.comp.map( 722 var children =
721 goog.bind(this.processOperationsInRow_, this)); 723 partition.comp.map(goog.bind(this.processOperationsInRow_, this));
722 if (partition.rel.every( 724 if (partition.rel.every(function(x) {
723 function(x) {return x.textContent == firstRel.textContent;})) { 725 return x.textContent == firstRel.textContent;
726 })) {
724 return this.makeBranchNode_( 727 return this.makeBranchNode_(
725 cvox.SemanticAttr.Type.RELSEQ, children, partition.rel, 728 cvox.SemanticAttr.Type.RELSEQ, children, partition.rel,
726 firstRel.textContent); 729 firstRel.textContent);
727 } 730 }
728 return this.makeBranchNode_( 731 return this.makeBranchNode_(
729 cvox.SemanticAttr.Type.MULTIREL, children, partition.rel); 732 cvox.SemanticAttr.Type.MULTIREL, children, partition.rel);
730 }; 733 };
731 734
732 735
733 /** 736 /**
734 * Constructs a syntax tree with operator precedence from a list nodes. 737 * Constructs a syntax tree with operator precedence from a list nodes.
735 * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes. 738 * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes.
736 * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. 739 * @return {!cvox.SemanticTree.Node} The root node of the syntax tree.
737 * @private 740 * @private
738 */ 741 */
739 cvox.SemanticTree.prototype.processOperationsInRow_ = function(nodes) { 742 cvox.SemanticTree.prototype.processOperationsInRow_ = function(nodes) {
740 if (nodes.length == 0) { 743 if (nodes.length == 0) {
741 return this.makeEmptyNode_(); 744 return this.makeEmptyNode_();
742 } 745 }
743 if (nodes.length == 1) { 746 if (nodes.length == 1) {
744 return nodes[0]; 747 return nodes[0];
745 } 748 }
746 749
747 var prefix = []; 750 var prefix = [];
748 while (nodes.length > 0 && 751 while (nodes.length > 0 && nodes[0].type == cvox.SemanticAttr.Type.OPERATOR) {
749 nodes[0].type == cvox.SemanticAttr.Type.OPERATOR) {
750 prefix.push(nodes.shift()); 752 prefix.push(nodes.shift());
751 } 753 }
752 // Pathological case: only operators in row. 754 // Pathological case: only operators in row.
753 if (nodes.length == 0) { 755 if (nodes.length == 0) {
754 return this.makePrefixNode_(prefix.pop(), prefix); 756 return this.makePrefixNode_(prefix.pop(), prefix);
755 } 757 }
756 if (nodes.length == 1) { 758 if (nodes.length == 1) {
757 return this.makePrefixNode_(nodes[0], prefix); 759 return this.makePrefixNode_(nodes[0], prefix);
758 } 760 }
759 761
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
803 } 805 }
804 806
805 var split = cvox.SemanticTree.sliceNodes_( 807 var split = cvox.SemanticTree.sliceNodes_(
806 nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR')); 808 nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR'));
807 809
808 if (split.head.length == 0) { 810 if (split.head.length == 0) {
809 prefixes.push(split.div); 811 prefixes.push(split.div);
810 return this.makeOperationsTree_(split.tail, root, lastop, prefixes); 812 return this.makeOperationsTree_(split.tail, root, lastop, prefixes);
811 } 813 }
812 814
813 var node = this.makePrefixNode_( 815 var node = this.makePrefixNode_(this.makeImplicitNode_(split.head), prefixes);
814 this.makeImplicitNode_(split.head), prefixes);
815 var newNode = this.appendOperand_(root, lastop, node); 816 var newNode = this.appendOperand_(root, lastop, node);
816 if (!split.div) { 817 if (!split.div) {
817 return newNode; 818 return newNode;
818 } 819 }
819 820
820 return this.makeOperationsTree_(split.tail, newNode, split.div, []); 821 return this.makeOperationsTree_(split.tail, newNode, split.div, []);
821 }; 822 };
822 823
823 // TODO (sorge) The following four functions could be combined into 824 // TODO (sorge) The following four functions could be combined into
824 // a single one. Currently it is clearer the way it is, though. 825 // a single one. Currently it is clearer the way it is, though.
825 /** 826 /**
826 * Appends an operand at the right place in an operator tree. 827 * 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} root The operator tree.
828 * @param {!cvox.SemanticTree.Node} op The operator node. 829 * @param {!cvox.SemanticTree.Node} op The operator node.
829 * @param {!cvox.SemanticTree.Node} node The node to be added. 830 * @param {!cvox.SemanticTree.Node} node The node to be added.
830 * @return {!cvox.SemanticTree.Node} The modified root node. 831 * @return {!cvox.SemanticTree.Node} The modified root node.
831 * @private 832 * @private
832 */ 833 */
833 cvox.SemanticTree.prototype.appendOperand_ = function(root, op, node) { 834 cvox.SemanticTree.prototype.appendOperand_ = function(root, op, node) {
834 // In general our operator tree will have the form that additions and 835 // In general our operator tree will have the form that additions and
835 // subtractions are stacked, while multiplications are subordinate. 836 // subtractions are stacked, while multiplications are subordinate.
836 if (root.type != cvox.SemanticAttr.Type.INFIXOP) { 837 if (root.type != cvox.SemanticAttr.Type.INFIXOP) {
837 return this.makeInfixNode_([root, node], op); 838 return this.makeInfixNode_([root, node], op);
838 } 839 }
839 if (this.appendExistingOperator_(root, op, node)) { 840 if (this.appendExistingOperator_(root, op, node)) {
840 return root; 841 return root;
841 } 842 }
842 return op.role == cvox.SemanticAttr.Role.MULTIPLICATION ? 843 return op.role == cvox.SemanticAttr.Role.MULTIPLICATION ?
843 this.appendMultiplicativeOp_(root, op, node) : 844 this.appendMultiplicativeOp_(root, op, node) :
844 this.appendAdditiveOp_(root, op, node); 845 this.appendAdditiveOp_(root, op, node);
845 }; 846 };
846 847
847 848
848 /** 849 /**
849 * Appends a multiplicative operator and operand. 850 * Appends a multiplicative operator and operand.
850 * @param {!cvox.SemanticTree.Node} root The root node. 851 * @param {!cvox.SemanticTree.Node} root The root node.
851 * @param {!cvox.SemanticTree.Node} op The operator node. 852 * @param {!cvox.SemanticTree.Node} op The operator node.
852 * @param {!cvox.SemanticTree.Node} node The operand node to be added. 853 * @param {!cvox.SemanticTree.Node} node The operand node to be added.
853 * @return {!cvox.SemanticTree.Node} The modified root node. 854 * @return {!cvox.SemanticTree.Node} The modified root node.
854 * @private 855 * @private
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 return false; 894 return false;
894 } 895 }
895 if (root.textContent == op.textContent) { 896 if (root.textContent == op.textContent) {
896 root.appendContentNode_(op); 897 root.appendContentNode_(op);
897 root.appendChild_(node); 898 root.appendChild_(node);
898 return true; 899 return true;
899 } 900 }
900 this.appendExistingOperator_( 901 this.appendExistingOperator_(
901 // Again, if this is an INFIXOP node, we know it has a child! 902 // Again, if this is an INFIXOP node, we know it has a child!
902 /** @type {!cvox.SemanticTree.Node} */ 903 /** @type {!cvox.SemanticTree.Node} */
903 (root.childNodes[root.childNodes.length - 1]), 904 (root.childNodes[root.childNodes.length - 1]), op, node);
904 op, node);
905 return false; 905 return false;
906 }; 906 };
907 907
908 908
909 // TODO (sorge) The following procedure needs a rational reconstruction. It 909 // TODO (sorge) The following procedure needs a rational reconstruction. It
910 // contains a number of similar cases which should be combined. 910 // contains a number of similar cases which should be combined.
911 /** 911 /**
912 * Combines delimited expressions in a list of nodes. 912 * Combines delimited expressions in a list of nodes.
913 * 913 *
914 * The basic idea of the heuristic is as follows: 914 * The basic idea of the heuristic is as follows:
(...skipping 24 matching lines...) Expand all
939 * @param {!Array<!Array<cvox.SemanticTree.Node>>} content FIFO queue content 939 * @param {!Array<!Array<cvox.SemanticTree.Node>>} content FIFO queue content
940 * between fences. 940 * between fences.
941 * @param {!Array<cvox.SemanticTree.Node>} openStack LIFO stack of open fences. 941 * @param {!Array<cvox.SemanticTree.Node>} openStack LIFO stack of open fences.
942 * @param {!Array<!Array<cvox.SemanticTree.Node>>} contentStack LIFO stack of 942 * @param {!Array<!Array<cvox.SemanticTree.Node>>} contentStack LIFO stack of
943 * content between fences yet to be processed. 943 * content between fences yet to be processed.
944 * @return {!Array<cvox.SemanticTree.Node>} A list of nodes with all fenced 944 * @return {!Array<cvox.SemanticTree.Node>} A list of nodes with all fenced
945 * expressions processed. 945 * expressions processed.
946 * @private 946 * @private
947 */ 947 */
948 cvox.SemanticTree.prototype.processFences_ = function( 948 cvox.SemanticTree.prototype.processFences_ = function(
949 fences, content, openStack, contentStack) { 949 fences, content, openStack, contentStack) {
950 // Base case 1: Everything is used up. 950 // Base case 1: Everything is used up.
951 if (fences.length == 0 && openStack.length == 0) { 951 if (fences.length == 0 && openStack.length == 0) {
952 return contentStack[0]; 952 return contentStack[0];
953 } 953 }
954 var openPred = cvox.SemanticTree.attrPred_('role', 'OPEN'); 954 var openPred = cvox.SemanticTree.attrPred_('role', 'OPEN');
955 // Base case 2: Only open and neutral fences are left on the stack. 955 // Base case 2: Only open and neutral fences are left on the stack.
956 if (fences.length == 0) { 956 if (fences.length == 0) {
957 // Basic idea: 957 // Basic idea:
958 // - make punctuation nodes from open fences 958 // - make punctuation nodes from open fences
959 // - combine as many neutral fences as possible, if the are not separated by 959 // - combine as many neutral fences as possible, if the are not separated by
960 // open fences. 960 // open fences.
961 // The idea is to allow for things like case statements etc. and not bury 961 // The idea is to allow for things like case statements etc. and not bury
962 // them inside a neutral fenced expression. 962 // them inside a neutral fenced expression.
963 // 963 //
964 // 0. We process the list from left to right. Hence the first element on the 964 // 0. We process the list from left to right. Hence the first element on the
965 // content stack are actually left most elements in the expression. 965 // content stack are actually left most elements in the expression.
966 // 1. Slice at open fence. 966 // 1. Slice at open fence.
967 // 2. On tail optimize for neutral fences. 967 // 2. On tail optimize for neutral fences.
968 // 3. Repeat until fence stack is exhausted. 968 // 3. Repeat until fence stack is exhausted.
969 // Push rightmost elements onto the result. 969 // Push rightmost elements onto the result.
970 var result = contentStack.shift(); 970 var result = contentStack.shift();
971 while (openStack.length > 0) { 971 while (openStack.length > 0) {
972 if (openPred(openStack[0])) { 972 if (openPred(openStack[0])) {
973 var firstOpen = openStack.shift(); 973 var firstOpen = openStack.shift();
974 cvox.SemanticTree.fenceToPunct_(firstOpen); 974 cvox.SemanticTree.fenceToPunct_(firstOpen);
975 result.push(firstOpen); 975 result.push(firstOpen);
976 } else { 976 } else {
977 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred); 977 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred);
978 var cutLength = split.head.length - 1; 978 var cutLength = split.head.length - 1;
979 var innerNodes = this.processNeutralFences_( 979 var innerNodes = this.processNeutralFences_(
980 split.head, contentStack.slice(0, cutLength)); 980 split.head, contentStack.slice(0, cutLength));
981 contentStack = contentStack.slice(cutLength); 981 contentStack = contentStack.slice(cutLength);
982 //var rightContent = contentStack.shift(); 982 // var rightContent = contentStack.shift();
983 result.push.apply(result, innerNodes); 983 result.push.apply(result, innerNodes);
984 //result.push.apply(result, rightContent); 984 // result.push.apply(result, rightContent);
985 if (split.div) { 985 if (split.div) {
986 split.tail.unshift(split.div); 986 split.tail.unshift(split.div);
987 } 987 }
988 openStack = split.tail; 988 openStack = split.tail;
989 } 989 }
990 result.push.apply(result, contentStack.shift()); 990 result.push.apply(result, contentStack.shift());
991 } 991 }
992 return result; 992 return result;
993 } 993 }
994 var lastOpen = openStack[openStack.length - 1]; 994 var lastOpen = openStack[openStack.length - 1];
995 var firstRole = fences[0].role; 995 var firstRole = fences[0].role;
996 // General opening case. 996 // General opening case.
997 // Either we have an open fence. 997 // Either we have an open fence.
998 if (firstRole == cvox.SemanticAttr.Role.OPEN || 998 if (firstRole == cvox.SemanticAttr.Role.OPEN ||
999 // Or we have a neutral fence that does not have a counter part. 999 // Or we have a neutral fence that does not have a counter part.
1000 (firstRole == cvox.SemanticAttr.Role.NEUTRAL && 1000 (firstRole == cvox.SemanticAttr.Role.NEUTRAL &&
1001 (!lastOpen || 1001 (!lastOpen || fences[0].textContent != lastOpen.textContent))) {
1002 fences[0].textContent != lastOpen.textContent))) {
1003 openStack.push(fences.shift()); 1002 openStack.push(fences.shift());
1004 contentStack.push(content.shift()); 1003 contentStack.push(content.shift());
1005 return this.processFences_(fences, content, openStack, contentStack); 1004 return this.processFences_(fences, content, openStack, contentStack);
1006 } 1005 }
1007 // General closing case. 1006 // General closing case.
1008 if (lastOpen && ( 1007 if (lastOpen &&
1009 // Closing fence for some opening fence. 1008 (
1010 (firstRole == cvox.SemanticAttr.Role.CLOSE && 1009 // Closing fence for some opening fence.
1011 lastOpen.role == cvox.SemanticAttr.Role.OPEN) || 1010 (firstRole == cvox.SemanticAttr.Role.CLOSE &&
1012 // Netural fence with exact counter part. 1011 lastOpen.role == cvox.SemanticAttr.Role.OPEN) ||
1013 (firstRole == cvox.SemanticAttr.Role.NEUTRAL && 1012 // Netural fence with exact counter part.
1014 fences[0].textContent == lastOpen.textContent))) { 1013 (firstRole == cvox.SemanticAttr.Role.NEUTRAL &&
1014 fences[0].textContent == lastOpen.textContent))) {
1015 var fenced = this.makeHorizontalFencedNode_( 1015 var fenced = this.makeHorizontalFencedNode_(
1016 openStack.pop(), fences.shift(), contentStack.pop()); 1016 openStack.pop(), fences.shift(), contentStack.pop());
1017 contentStack.push(contentStack.pop().concat([fenced], content.shift())); 1017 contentStack.push(contentStack.pop().concat([fenced], content.shift()));
1018 return this.processFences_(fences, content, openStack, contentStack); 1018 return this.processFences_(fences, content, openStack, contentStack);
1019 } 1019 }
1020 // Closing with a neutral fence on the stack. 1020 // Closing with a neutral fence on the stack.
1021 if (lastOpen && firstRole == cvox.SemanticAttr.Role.CLOSE && 1021 if (lastOpen && firstRole == cvox.SemanticAttr.Role.CLOSE &&
1022 lastOpen.role == cvox.SemanticAttr.Role.NEUTRAL && 1022 lastOpen.role == cvox.SemanticAttr.Role.NEUTRAL &&
1023 openStack.some(openPred)) { 1023 openStack.some(openPred)) {
1024 // Steps of the algorithm: 1024 // Steps of the algorithm:
1025 // 1. Split list at right most opening bracket. 1025 // 1. Split list at right most opening bracket.
1026 // 2. Cut content list at corresponding length. 1026 // 2. Cut content list at corresponding length.
1027 // 3. Optimise the neutral fences. 1027 // 3. Optimise the neutral fences.
1028 // 4. Make fenced node. 1028 // 4. Make fenced node.
1029 // 1029 //
1030 // Careful, this reverses openStack! 1030 // Careful, this reverses openStack!
1031 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred, true); 1031 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred, true);
1032 // We know that 1032 // We know that
1033 // (a) div & tail exist, 1033 // (a) div & tail exist,
1034 // (b) all are combined in this step into a single fenced node, 1034 // (b) all are combined in this step into a single fenced node,
1035 // (c) head is the new openStack, 1035 // (c) head is the new openStack,
1036 // (d) the new contentStack is remainder of contentStack + new fenced node + 1036 // (d) the new contentStack is remainder of contentStack + new fenced node +
1037 // shift of content. 1037 // shift of content.
1038 var rightContent = contentStack.pop(); 1038 var rightContent = contentStack.pop();
1039 var cutLength = contentStack.length - split.tail.length + 1; 1039 var cutLength = contentStack.length - split.tail.length + 1;
1040 var innerNodes = this.processNeutralFences_( 1040 var innerNodes =
1041 split.tail, contentStack.slice(cutLength)); 1041 this.processNeutralFences_(split.tail, contentStack.slice(cutLength));
1042 contentStack = contentStack.slice(0, cutLength); 1042 contentStack = contentStack.slice(0, cutLength);
1043 var fenced = this.makeHorizontalFencedNode_( 1043 var fenced = this.makeHorizontalFencedNode_(
1044 split.div, fences.shift(), 1044 split.div, fences.shift(),
1045 contentStack.pop().concat(innerNodes, rightContent)); 1045 contentStack.pop().concat(innerNodes, rightContent));
1046 contentStack.push(contentStack.pop().concat([fenced], content.shift())); 1046 contentStack.push(contentStack.pop().concat([fenced], content.shift()));
1047 return this.processFences_(fences, content, split.head, contentStack); 1047 return this.processFences_(fences, content, split.head, contentStack);
1048 } 1048 }
1049 // Final Case: A singular closing fence. 1049 // Final Case: A singular closing fence.
1050 // We turn the fence into a punctuation. 1050 // We turn the fence into a punctuation.
1051 var fenced = fences.shift(); 1051 var fenced = fences.shift();
(...skipping 13 matching lines...) Expand all
1065 * nodes. 1065 * nodes.
1066 * @private 1066 * @private
1067 */ 1067 */
1068 cvox.SemanticTree.prototype.processNeutralFences_ = function(fences, content) { 1068 cvox.SemanticTree.prototype.processNeutralFences_ = function(fences, content) {
1069 if (fences.length == 0) { 1069 if (fences.length == 0) {
1070 return fences; 1070 return fences;
1071 } 1071 }
1072 if (fences.length == 1) { 1072 if (fences.length == 1) {
1073 cvox.SemanticTree.fenceToPunct_(fences[0]); 1073 cvox.SemanticTree.fenceToPunct_(fences[0]);
1074 return fences; 1074 return fences;
1075 } 1075 }
1076 var firstFence = fences.shift(); 1076 var firstFence = fences.shift();
1077 var split = cvox.SemanticTree.sliceNodes_( 1077 var split = cvox.SemanticTree.sliceNodes_(fences, function(x) {
1078 fences, function(x) {return x.textContent == firstFence.textContent;}); 1078 return x.textContent == firstFence.textContent;
1079 });
1079 if (!split.div) { 1080 if (!split.div) {
1080 cvox.SemanticTree.fenceToPunct_(firstFence); 1081 cvox.SemanticTree.fenceToPunct_(firstFence);
1081 var restContent = content.shift(); 1082 var restContent = content.shift();
1082 restContent.unshift(firstFence); 1083 restContent.unshift(firstFence);
1083 return restContent.concat(this.processNeutralFences_(fences, content)); 1084 return restContent.concat(this.processNeutralFences_(fences, content));
1084 } 1085 }
1085 var newContent = this.combineFencedContent_( 1086 var newContent =
1086 firstFence, split.div, split.head, content); 1087 this.combineFencedContent_(firstFence, split.div, split.head, content);
1087 if (split.tail.length > 0) { 1088 if (split.tail.length > 0) {
1088 var leftContent = newContent.shift(); 1089 var leftContent = newContent.shift();
1089 var result = this.processNeutralFences_(split.tail, newContent); 1090 var result = this.processNeutralFences_(split.tail, newContent);
1090 return leftContent.concat(result); 1091 return leftContent.concat(result);
1091 } 1092 }
1092 return newContent[0]; 1093 return newContent[0];
1093 }; 1094 };
1094 1095
1095 1096
1096 /** 1097 /**
(...skipping 10 matching lines...) Expand all
1107 * remainder. 1108 * remainder.
1108 * @return {!Array<!Array<cvox.SemanticTree.Node>>} List of content nodes 1109 * @return {!Array<!Array<cvox.SemanticTree.Node>>} List of content nodes
1109 * where the first is the fully fenced node wrt. the given left and right 1110 * where the first is the fully fenced node wrt. the given left and right
1110 * fence. 1111 * fence.
1111 * @private 1112 * @private
1112 */ 1113 */
1113 cvox.SemanticTree.prototype.combineFencedContent_ = function( 1114 cvox.SemanticTree.prototype.combineFencedContent_ = function(
1114 leftFence, rightFence, midFences, content) { 1115 leftFence, rightFence, midFences, content) {
1115 1116
1116 if (midFences.length == 0) { 1117 if (midFences.length == 0) {
1117 var fenced = this.makeHorizontalFencedNode_( 1118 var fenced =
1118 leftFence, rightFence, content.shift()); 1119 this.makeHorizontalFencedNode_(leftFence, rightFence, content.shift());
1119 content.unshift(fenced); 1120 content.unshift(fenced);
1120 return content; 1121 return content;
1121 } 1122 }
1122 1123
1123 var leftContent = content.shift(); 1124 var leftContent = content.shift();
1124 var cutLength = midFences.length - 1; 1125 var cutLength = midFences.length - 1;
1125 var midContent = content.slice(0, cutLength); 1126 var midContent = content.slice(0, cutLength);
1126 content = content.slice(cutLength); 1127 content = content.slice(cutLength);
1127 var rightContent = content.shift(); 1128 var rightContent = content.shift();
1128 var innerNodes = this.processNeutralFences_(midFences, midContent); 1129 var innerNodes = this.processNeutralFences_(midFences, midContent);
1129 leftContent.push.apply(leftContent, innerNodes); 1130 leftContent.push.apply(leftContent, innerNodes);
1130 leftContent.push.apply(leftContent, rightContent); 1131 leftContent.push.apply(leftContent, rightContent);
1131 var fenced = this.makeHorizontalFencedNode_( 1132 var fenced =
1132 leftFence, rightFence, leftContent); 1133 this.makeHorizontalFencedNode_(leftFence, rightFence, leftContent);
1133 if (content.length > 0) { 1134 if (content.length > 0) {
1134 content[0].unshift(fenced); 1135 content[0].unshift(fenced);
1135 } else { 1136 } else {
1136 content = [[fenced]]; 1137 content = [[fenced]];
1137 } 1138 }
1138 return content; 1139 return content;
1139 }; 1140 };
1140 1141
1141 1142
1142 /** 1143 /**
1143 * Rewrite fences into punctuation. This is done with any "leftover" fence. 1144 * Rewrite fences into punctuation. This is done with any "leftover" fence.
1144 * @param {cvox.SemanticTree.Node} fence Fence. 1145 * @param {cvox.SemanticTree.Node} fence Fence.
1145 * @private 1146 * @private
1146 */ 1147 */
1147 cvox.SemanticTree.fenceToPunct_ = function(fence) { 1148 cvox.SemanticTree.fenceToPunct_ = function(fence) {
1148 fence.type = cvox.SemanticAttr.Type.PUNCTUATION; 1149 fence.type = cvox.SemanticAttr.Type.PUNCTUATION;
1149 switch (fence.role) { 1150 switch (fence.role) {
1150 case cvox.SemanticAttr.Role.NEUTRAL: 1151 case cvox.SemanticAttr.Role.NEUTRAL:
1151 fence.role = cvox.SemanticAttr.Role.VBAR; 1152 fence.role = cvox.SemanticAttr.Role.VBAR;
1152 break; 1153 break;
1153 case cvox.SemanticAttr.Role.OPEN: 1154 case cvox.SemanticAttr.Role.OPEN:
1154 fence.role = cvox.SemanticAttr.Role.OPENFENCE; 1155 fence.role = cvox.SemanticAttr.Role.OPENFENCE;
1155 break; 1156 break;
1156 case cvox.SemanticAttr.Role.CLOSE: 1157 case cvox.SemanticAttr.Role.CLOSE:
1157 fence.role = cvox.SemanticAttr.Role.CLOSEFENCE; 1158 fence.role = cvox.SemanticAttr.Role.CLOSEFENCE;
1158 break; 1159 break;
1159 } 1160 }
1160 }; 1161 };
1161 1162
1162 1163
1163 /** 1164 /**
1164 * Create a fenced node. 1165 * Create a fenced node.
1165 * @param {cvox.SemanticTree.Node} ofence Opening fence. 1166 * @param {cvox.SemanticTree.Node} ofence Opening fence.
1166 * @param {cvox.SemanticTree.Node} cfence Closing fence. 1167 * @param {cvox.SemanticTree.Node} cfence Closing fence.
1167 * @param {!Array<cvox.SemanticTree.Node>} content The content 1168 * @param {!Array<cvox.SemanticTree.Node>} content The content
1168 * between the fences. 1169 * between the fences.
(...skipping 18 matching lines...) Expand all
1187 * Combines sequences of punctuated expressions in a list of nodes. 1188 * Combines sequences of punctuated expressions in a list of nodes.
1188 * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes. 1189 * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes.
1189 * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes. 1190 * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes.
1190 * @private 1191 * @private
1191 */ 1192 */
1192 cvox.SemanticTree.prototype.getPunctuationInRow_ = function(nodes) { 1193 cvox.SemanticTree.prototype.getPunctuationInRow_ = function(nodes) {
1193 // For now we just make a punctuation node with a particular role. This is 1194 // For now we just make a punctuation node with a particular role. This is
1194 // similar to an mrow. The only exception are ellipses, which we assume to be 1195 // similar to an mrow. The only exception are ellipses, which we assume to be
1195 // in lieu of identifiers. 1196 // in lieu of identifiers.
1196 // In addition we keep the single punctuation nodes as content. 1197 // In addition we keep the single punctuation nodes as content.
1197 var partition = cvox.SemanticTree.partitionNodes_( 1198 var partition = cvox.SemanticTree.partitionNodes_(nodes, function(x) {
1198 nodes, function(x) { 1199 return cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(x) &&
1199 return cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(x) && 1200 !cvox.SemanticTree.attrPred_('role', 'ELLIPSIS')(x);
1200 !cvox.SemanticTree.attrPred_('role', 'ELLIPSIS')(x);}); 1201 });
1201 if (partition.rel.length == 0) { 1202 if (partition.rel.length == 0) {
1202 return nodes; 1203 return nodes;
1203 } 1204 }
1204 var newNodes = []; 1205 var newNodes = [];
1205 var firstComp = partition.comp.shift(); 1206 var firstComp = partition.comp.shift();
1206 if (firstComp.length > 0) { 1207 if (firstComp.length > 0) {
1207 newNodes.push(this.processRow_(firstComp)); 1208 newNodes.push(this.processRow_(firstComp));
1208 } 1209 }
1209 var relCounter = 0; 1210 var relCounter = 0;
1210 while (partition.comp.length > 0) { 1211 while (partition.comp.length > 0) {
(...skipping 17 matching lines...) Expand all
1228 * @private 1229 * @private
1229 */ 1230 */
1230 cvox.SemanticTree.prototype.makePunctuatedNode_ = function( 1231 cvox.SemanticTree.prototype.makePunctuatedNode_ = function(
1231 nodes, punctuations) { 1232 nodes, punctuations) {
1232 var newNode = this.makeBranchNode_( 1233 var newNode = this.makeBranchNode_(
1233 cvox.SemanticAttr.Type.PUNCTUATED, nodes, punctuations); 1234 cvox.SemanticAttr.Type.PUNCTUATED, nodes, punctuations);
1234 1235
1235 if (punctuations.length == 1 && 1236 if (punctuations.length == 1 &&
1236 nodes[0].type == cvox.SemanticAttr.Type.PUNCTUATION) { 1237 nodes[0].type == cvox.SemanticAttr.Type.PUNCTUATION) {
1237 newNode.role = cvox.SemanticAttr.Role.STARTPUNCT; 1238 newNode.role = cvox.SemanticAttr.Role.STARTPUNCT;
1238 } else if (punctuations.length == 1 && 1239 } else if (
1240 punctuations.length == 1 &&
1239 nodes[nodes.length - 1].type == cvox.SemanticAttr.Type.PUNCTUATION) { 1241 nodes[nodes.length - 1].type == cvox.SemanticAttr.Type.PUNCTUATION) {
1240 newNode.role = cvox.SemanticAttr.Role.ENDPUNCT; 1242 newNode.role = cvox.SemanticAttr.Role.ENDPUNCT;
1241 } else { 1243 } else {
1242 newNode.role = cvox.SemanticAttr.Role.SEQUENCE; 1244 newNode.role = cvox.SemanticAttr.Role.SEQUENCE;
1243 } 1245 }
1244 return newNode; 1246 return newNode;
1245 }; 1247 };
1246 1248
1247 1249
1248 /** 1250 /**
(...skipping 13 matching lines...) Expand all
1262 cvox.SemanticTree.attrPred_('type', 'LIMBOTH')(center) || 1264 cvox.SemanticTree.attrPred_('type', 'LIMBOTH')(center) ||
1263 cvox.SemanticTree.attrPred_('type', 'LIMLOWER')(center) || 1265 cvox.SemanticTree.attrPred_('type', 'LIMLOWER')(center) ||
1264 cvox.SemanticTree.attrPred_('type', 'LIMUPPER')(center) || 1266 cvox.SemanticTree.attrPred_('type', 'LIMUPPER')(center) ||
1265 (isFunction && cvox.SemanticTree.attrPred_('role', 'LIMFUNC')(center)); 1267 (isFunction && cvox.SemanticTree.attrPred_('role', 'LIMFUNC')(center));
1266 var type = cvox.SemanticAttr.Type.UNKNOWN; 1268 var type = cvox.SemanticAttr.Type.UNKNOWN;
1267 // TODO (sorge) Make use of the difference in information on sub vs under etc. 1269 // TODO (sorge) Make use of the difference in information on sub vs under etc.
1268 if (isLimit) { 1270 if (isLimit) {
1269 switch (mmlTag) { 1271 switch (mmlTag) {
1270 case 'MSUB': 1272 case 'MSUB':
1271 case 'MUNDER': 1273 case 'MUNDER':
1272 type = cvox.SemanticAttr.Type.LIMLOWER; 1274 type = cvox.SemanticAttr.Type.LIMLOWER;
1273 break; 1275 break;
1274 case 'MSUP': 1276 case 'MSUP':
1275 case 'MOVER': 1277 case 'MOVER':
1276 type = cvox.SemanticAttr.Type.LIMUPPER; 1278 type = cvox.SemanticAttr.Type.LIMUPPER;
1277 break; 1279 break;
1278 case 'MSUBSUP': 1280 case 'MSUBSUP':
1279 case 'MUNDEROVER': 1281 case 'MUNDEROVER':
1280 type = cvox.SemanticAttr.Type.LIMBOTH; 1282 type = cvox.SemanticAttr.Type.LIMBOTH;
1281 break; 1283 break;
1282 } 1284 }
1283 } else { 1285 } else {
1284 switch (mmlTag) { 1286 switch (mmlTag) {
1285 case 'MSUB': 1287 case 'MSUB':
1286 type = cvox.SemanticAttr.Type.SUBSCRIPT; 1288 type = cvox.SemanticAttr.Type.SUBSCRIPT;
1287 break; 1289 break;
1288 case 'MSUP': 1290 case 'MSUP':
1289 type = cvox.SemanticAttr.Type.SUPERSCRIPT; 1291 type = cvox.SemanticAttr.Type.SUPERSCRIPT;
1290 break; 1292 break;
1291 case 'MSUBSUP': 1293 case 'MSUBSUP':
1292 var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.SUBSCRIPT, 1294 var innerNode = this.makeBranchNode_(
1293 [center, children[1]], []); 1295 cvox.SemanticAttr.Type.SUBSCRIPT, [center, children[1]], []);
1294 innerNode.role = center.role; 1296 innerNode.role = center.role;
1295 children = [innerNode, children[2]]; 1297 children = [innerNode, children[2]];
1296 type = cvox.SemanticAttr.Type.SUPERSCRIPT; 1298 type = cvox.SemanticAttr.Type.SUPERSCRIPT;
1297 break; 1299 break;
1298 case 'MOVER': 1300 case 'MOVER':
1299 type = cvox.SemanticAttr.Type.OVERSCORE; 1301 type = cvox.SemanticAttr.Type.OVERSCORE;
1300 break; 1302 break;
1301 case 'MUNDER': 1303 case 'MUNDER':
1302 type = cvox.SemanticAttr.Type.UNDERSCORE; 1304 type = cvox.SemanticAttr.Type.UNDERSCORE;
1303 break; 1305 break;
1304 case 'MUNDEROVER': 1306 case 'MUNDEROVER':
1305 default: 1307 default:
1306 var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.UNDERSCORE, 1308 var innerNode = this.makeBranchNode_(
1307 [center, children[1]], []); 1309 cvox.SemanticAttr.Type.UNDERSCORE, [center, children[1]], []);
1308 innerNode.role = center.role; 1310 innerNode.role = center.role;
1309 children = [innerNode, children[2]]; 1311 children = [innerNode, children[2]];
1310 type = cvox.SemanticAttr.Type.OVERSCORE; 1312 type = cvox.SemanticAttr.Type.OVERSCORE;
1311 break; 1313 break;
1312 } 1314 }
1313 } 1315 }
1314 var newNode = this.makeBranchNode_(type, children, []); 1316 var newNode = this.makeBranchNode_(type, children, []);
1315 newNode.role = center.role; 1317 newNode.role = center.role;
1316 return newNode; 1318 return newNode;
1317 }; 1319 };
1318 1320
1319 1321
1320 /** 1322 /**
1321 * Recursive method to accumulate function expressions. 1323 * Recursive method to accumulate function expressions.
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
1364 * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of 1366 * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of
1365 * nodes. They can useful to look ahead if there is an explicit function 1367 * nodes. They can useful to look ahead if there is an explicit function
1366 * application. If there is one, it will be destructively removed! 1368 * application. If there is one, it will be destructively removed!
1367 * @return {!string} The string specifying the heuristic. 1369 * @return {!string} The string specifying the heuristic.
1368 * @private 1370 * @private
1369 */ 1371 */
1370 cvox.SemanticTree.classifyFunction_ = function(funcNode, restNodes) { 1372 cvox.SemanticTree.classifyFunction_ = function(funcNode, restNodes) {
1371 // We do not allow double function application. This is not lambda calculus! 1373 // We do not allow double function application. This is not lambda calculus!
1372 if (funcNode.type == cvox.SemanticAttr.Type.APPL || 1374 if (funcNode.type == cvox.SemanticAttr.Type.APPL ||
1373 funcNode.type == cvox.SemanticAttr.Type.BIGOP || 1375 funcNode.type == cvox.SemanticAttr.Type.BIGOP ||
1374 funcNode.type == cvox.SemanticAttr.Type.INTEGRAL) { 1376 funcNode.type == cvox.SemanticAttr.Type.INTEGRAL) {
1375 return ''; 1377 return '';
1376 } 1378 }
1377 // Find and remove explicit function applications. 1379 // Find and remove explicit function applications.
1378 // We now treat funcNode as a prefix function, regardless of what its actual 1380 // We now treat funcNode as a prefix function, regardless of what its actual
1379 // content is. 1381 // content is.
1380 if (restNodes[0] && 1382 if (restNodes[0] &&
1381 restNodes[0].textContent == cvox.SemanticAttr.functionApplication()) { 1383 restNodes[0].textContent == cvox.SemanticAttr.functionApplication()) {
1382 // Remove explicit function application. This is destructive on the 1384 // Remove explicit function application. This is destructive on the
1383 // underlying list. 1385 // underlying list.
1384 restNodes.shift(); 1386 restNodes.shift();
1385 cvox.SemanticTree.propagatePrefixFunc_(funcNode); 1387 cvox.SemanticTree.propagatePrefixFunc_(funcNode);
1386 return 'prefix'; 1388 return 'prefix';
1387 } 1389 }
1388 switch (funcNode.role) { 1390 switch (funcNode.role) {
1389 case cvox.SemanticAttr.Role.INTEGRAL: 1391 case cvox.SemanticAttr.Role.INTEGRAL:
1390 return 'integral'; 1392 return 'integral';
1391 break; 1393 break;
1392 case cvox.SemanticAttr.Role.SUM: 1394 case cvox.SemanticAttr.Role.SUM:
1393 return 'bigop'; 1395 return 'bigop';
1394 break; 1396 break;
1395 case cvox.SemanticAttr.Role.PREFIXFUNC: 1397 case cvox.SemanticAttr.Role.PREFIXFUNC:
1396 case cvox.SemanticAttr.Role.LIMFUNC: 1398 case cvox.SemanticAttr.Role.LIMFUNC:
1397 return 'prefix'; 1399 return 'prefix';
1398 break; 1400 break;
1399 default: 1401 default:
1400 if (funcNode.type == cvox.SemanticAttr.Type.IDENTIFIER) { 1402 if (funcNode.type == cvox.SemanticAttr.Type.IDENTIFIER) {
1401 return 'simple'; 1403 return 'simple';
1402 } 1404 }
1403 } 1405 }
1404 return ''; 1406 return '';
1405 }; 1407 };
1406 1408
1407 1409
1408 /** 1410 /**
1409 * Propagates a prefix function role in a node. 1411 * Propagates a prefix function role in a node.
1410 * @param {cvox.SemanticTree.Node} funcNode The node whose role is to be 1412 * @param {cvox.SemanticTree.Node} funcNode The node whose role is to be
1411 * rewritten. 1413 * rewritten.
1412 * @private 1414 * @private
(...skipping 13 matching lines...) Expand all
1426 * @param {!Array<cvox.SemanticTree.Node>} rest List of nodes to choose 1428 * @param {!Array<cvox.SemanticTree.Node>} rest List of nodes to choose
1427 * arguments from. 1429 * arguments from.
1428 * @param {string} heuristic The heuristic to follow. 1430 * @param {string} heuristic The heuristic to follow.
1429 * @return {!Array<!cvox.SemanticTree.Node>} The function and the remainder of 1431 * @return {!Array<!cvox.SemanticTree.Node>} The function and the remainder of
1430 * the rest list. 1432 * the rest list.
1431 * @private 1433 * @private
1432 */ 1434 */
1433 cvox.SemanticTree.prototype.getFunctionArgs_ = function(func, rest, heuristic) { 1435 cvox.SemanticTree.prototype.getFunctionArgs_ = function(func, rest, heuristic) {
1434 switch (heuristic) { 1436 switch (heuristic) {
1435 case 'integral': 1437 case 'integral':
1436 var components = this.getIntegralArgs_(rest); 1438 var components = this.getIntegralArgs_(rest);
1437 var integrand = this.processRow_(components.integrand); 1439 var integrand = this.processRow_(components.integrand);
1438 var funcNode = this.makeIntegralNode_(func, integrand, components.intvar); 1440 var funcNode = this.makeIntegralNode_(func, integrand, components.intvar);
1439 components.rest.unshift(funcNode); 1441 components.rest.unshift(funcNode);
1440 return components.rest; 1442 return components.rest;
1441 break; 1443 break;
1442 case 'prefix': 1444 case 'prefix':
1443 if (rest[0] && rest[0].type == cvox.SemanticAttr.Type.FENCED) { 1445 if (rest[0] && rest[0].type == cvox.SemanticAttr.Type.FENCED) {
1444 funcNode = this.makeFunctionNode_( 1446 funcNode = this.makeFunctionNode_(
1445 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); 1447 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift()));
1446 rest.unshift(funcNode); 1448 rest.unshift(funcNode);
1449 return rest;
1450 }
1451 case 'bigop':
1452 var partition = cvox.SemanticTree.sliceNodes_(
1453 rest, cvox.SemanticTree.prefixFunctionBoundary_);
1454 var arg = this.processRow_(partition.head);
1455 if (heuristic == 'prefix') {
1456 funcNode = this.makeFunctionNode_(func, arg);
1457 } else {
1458 funcNode = this.makeBigOpNode_(func, arg);
1459 }
1460 if (partition.div) {
1461 partition.tail.unshift(partition.div);
1462 }
1463 partition.tail.unshift(funcNode);
1464 return partition.tail;
1465 break;
1466 case 'simple':
1467 if (rest.length == 0) {
1468 return [func];
1469 }
1470 var firstArg = rest[0];
1471 if (firstArg.type == cvox.SemanticAttr.Type.FENCED &&
1472 firstArg.role != cvox.SemanticAttr.Role.NEUTRAL &&
1473 this.simpleFunctionHeuristic_(firstArg)) {
1474 funcNode = this.makeFunctionNode_(
1475 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift()));
1476 rest.unshift(funcNode);
1477 return rest;
1478 }
1479 rest.unshift(func);
1447 return rest; 1480 return rest;
1448 } 1481 break;
1449 case 'bigop':
1450 var partition = cvox.SemanticTree.sliceNodes_(
1451 rest, cvox.SemanticTree.prefixFunctionBoundary_);
1452 var arg = this.processRow_(partition.head);
1453 if (heuristic == 'prefix') {
1454 funcNode = this.makeFunctionNode_(func, arg);
1455 } else {
1456 funcNode = this.makeBigOpNode_(func, arg);
1457 }
1458 if (partition.div) {
1459 partition.tail.unshift(partition.div);
1460 }
1461 partition.tail.unshift(funcNode);
1462 return partition.tail;
1463 break;
1464 case 'simple':
1465 if (rest.length == 0) {
1466 return [func];
1467 }
1468 var firstArg = rest[0];
1469 if (firstArg.type == cvox.SemanticAttr.Type.FENCED &&
1470 firstArg.role != cvox.SemanticAttr.Role.NEUTRAL &&
1471 this.simpleFunctionHeuristic_(firstArg)) {
1472 funcNode = this.makeFunctionNode_(
1473 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift()));
1474 rest.unshift(funcNode);
1475 return rest;
1476 }
1477 rest.unshift(func);
1478 return rest;
1479 break;
1480 } 1482 }
1481 return []; 1483 return [];
1482 }; 1484 };
1483 1485
1484 1486
1485 /** 1487 /**
1486 * Tail recursive function to obtain integral arguments. 1488 * Tail recursive function to obtain integral arguments.
1487 * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to take 1489 * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to take
1488 * arguments from. 1490 * arguments from.
1489 * @param {Array<cvox.SemanticTree.Node>=} args List of integral arguments. 1491 * @param {Array<cvox.SemanticTree.Node>=} args List of integral arguments.
(...skipping 12 matching lines...) Expand all
1502 var firstNode = nodes[0]; 1504 var firstNode = nodes[0];
1503 if (cvox.SemanticTree.generalFunctionBoundary_(firstNode)) { 1505 if (cvox.SemanticTree.generalFunctionBoundary_(firstNode)) {
1504 return {integrand: args, intvar: null, rest: nodes}; 1506 return {integrand: args, intvar: null, rest: nodes};
1505 } 1507 }
1506 if (cvox.SemanticTree.integralDxBoundarySingle_(firstNode)) { 1508 if (cvox.SemanticTree.integralDxBoundarySingle_(firstNode)) {
1507 return {integrand: args, intvar: firstNode, rest: nodes.slice(1)}; 1509 return {integrand: args, intvar: firstNode, rest: nodes.slice(1)};
1508 } 1510 }
1509 if (nodes[1] && cvox.SemanticTree.integralDxBoundary_(firstNode, nodes[1])) { 1511 if (nodes[1] && cvox.SemanticTree.integralDxBoundary_(firstNode, nodes[1])) {
1510 var comma = this.createNode_(); 1512 var comma = this.createNode_();
1511 comma.updateContent_(cvox.SemanticAttr.invisibleComma()); 1513 comma.updateContent_(cvox.SemanticAttr.invisibleComma());
1512 var intvar = this.makePunctuatedNode_( 1514 var intvar =
1513 [firstNode, comma, nodes[1]], [comma]); 1515 this.makePunctuatedNode_([firstNode, comma, nodes[1]], [comma]);
1514 intvar.role = cvox.SemanticAttr.Role.INTEGRAL; 1516 intvar.role = cvox.SemanticAttr.Role.INTEGRAL;
1515 return {integrand: args, intvar: intvar, rest: nodes.slice(2)}; 1517 return {integrand: args, intvar: intvar, rest: nodes.slice(2)};
1516 } 1518 }
1517 args.push(nodes.shift()); 1519 args.push(nodes.shift());
1518 return this.getIntegralArgs_(nodes, args); 1520 return this.getIntegralArgs_(nodes, args);
1519 }; 1521 };
1520 1522
1521 1523
1522 /** 1524 /**
1523 * Create a function node. 1525 * Create a function node.
1524 * @param {!cvox.SemanticTree.Node} func The function operator. 1526 * @param {!cvox.SemanticTree.Node} func The function operator.
1525 * @param {!cvox.SemanticTree.Node} arg The argument. 1527 * @param {!cvox.SemanticTree.Node} arg The argument.
1526 * @return {!cvox.SemanticTree.Node} The new function node. 1528 * @return {!cvox.SemanticTree.Node} The new function node.
1527 * @private 1529 * @private
1528 */ 1530 */
1529 cvox.SemanticTree.prototype.makeFunctionNode_ = function(func, arg) { 1531 cvox.SemanticTree.prototype.makeFunctionNode_ = function(func, arg) {
1530 var applNode = this.createNode_(); 1532 var applNode = this.createNode_();
1531 applNode.updateContent_(cvox.SemanticAttr.functionApplication()); 1533 applNode.updateContent_(cvox.SemanticAttr.functionApplication());
1532 applNode.type = cvox.SemanticAttr.Type.PUNCTUATION; 1534 applNode.type = cvox.SemanticAttr.Type.PUNCTUATION;
1533 applNode.role = cvox.SemanticAttr.Role.APPLICATION; 1535 applNode.role = cvox.SemanticAttr.Role.APPLICATION;
1534 var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.APPL, [func, arg], 1536 var newNode = this.makeBranchNode_(
1535 [applNode]); 1537 cvox.SemanticAttr.Type.APPL, [func, arg], [applNode]);
1536 newNode.role = func.role; 1538 newNode.role = func.role;
1537 return newNode; 1539 return newNode;
1538 }; 1540 };
1539 1541
1540 1542
1541 /** 1543 /**
1542 * Create a big operator node. 1544 * Create a big operator node.
1543 * @param {!cvox.SemanticTree.Node} bigOp The big operator. 1545 * @param {!cvox.SemanticTree.Node} bigOp The big operator.
1544 * @param {!cvox.SemanticTree.Node} arg The argument. 1546 * @param {!cvox.SemanticTree.Node} arg The argument.
1545 * @return {!cvox.SemanticTree.Node} The new big operator node. 1547 * @return {!cvox.SemanticTree.Node} The new big operator node.
1546 * @private 1548 * @private
1547 */ 1549 */
1548 cvox.SemanticTree.prototype.makeBigOpNode_ = function(bigOp, arg) { 1550 cvox.SemanticTree.prototype.makeBigOpNode_ = function(bigOp, arg) {
1549 var newNode = this.makeBranchNode_( 1551 var newNode =
1550 cvox.SemanticAttr.Type.BIGOP, [bigOp, arg], []); 1552 this.makeBranchNode_(cvox.SemanticAttr.Type.BIGOP, [bigOp, arg], []);
1551 newNode.role = bigOp.role; 1553 newNode.role = bigOp.role;
1552 return newNode; 1554 return newNode;
1553 }; 1555 };
1554 1556
1555 1557
1556 /** 1558 /**
1557 * Create an integral node. It has three children: integral, integrand and 1559 * Create an integral node. It has three children: integral, integrand and
1558 * integration variable. The latter two can be omitted. 1560 * integration variable. The latter two can be omitted.
1559 * @param {!cvox.SemanticTree.Node} integral The integral operator. 1561 * @param {!cvox.SemanticTree.Node} integral The integral operator.
1560 * @param {cvox.SemanticTree.Node} integrand The integrand. 1562 * @param {cvox.SemanticTree.Node} integrand The integrand.
1561 * @param {cvox.SemanticTree.Node} intvar The integral variable. 1563 * @param {cvox.SemanticTree.Node} intvar The integral variable.
1562 * @return {!cvox.SemanticTree.Node} The new integral node. 1564 * @return {!cvox.SemanticTree.Node} The new integral node.
1563 * @private 1565 * @private
1564 */ 1566 */
1565 cvox.SemanticTree.prototype.makeIntegralNode_ = function( 1567 cvox.SemanticTree.prototype.makeIntegralNode_ = function(
1566 integral, integrand, intvar) { 1568 integral, integrand, intvar) {
1567 integrand = integrand || this.makeEmptyNode_(); 1569 integrand = integrand || this.makeEmptyNode_();
1568 intvar = intvar || this.makeEmptyNode_(); 1570 intvar = intvar || this.makeEmptyNode_();
1569 var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.INTEGRAL, 1571 var newNode = this.makeBranchNode_(
1570 [integral, integrand, intvar], []); 1572 cvox.SemanticAttr.Type.INTEGRAL, [integral, integrand, intvar], []);
1571 newNode.role = integral.role; 1573 newNode.role = integral.role;
1572 return newNode; 1574 return newNode;
1573 }; 1575 };
1574 1576
1575 1577
1576 /** 1578 /**
1577 * Predicate implementing the boundary criteria for simple functions: 1579 * Predicate implementing the boundary criteria for simple functions:
1578 * 1580 *
1579 * @param {!cvox.SemanticTree.Node} node A semantic node of type fenced. 1581 * @param {!cvox.SemanticTree.Node} node A semantic node of type fenced.
1580 * @return {boolean} True if the node meets the boundary criteria. 1582 * @return {boolean} True if the node meets the boundary criteria.
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1618 1620
1619 1621
1620 /** 1622 /**
1621 * Predicate implementing the boundary criteria for integrals dx on two nodes. 1623 * Predicate implementing the boundary criteria for integrals dx on two nodes.
1622 * @param {cvox.SemanticTree.Node} firstNode A semantic node. 1624 * @param {cvox.SemanticTree.Node} firstNode A semantic node.
1623 * @param {cvox.SemanticTree.Node} secondNode The direct neighbour of first 1625 * @param {cvox.SemanticTree.Node} secondNode The direct neighbour of first
1624 * Node. 1626 * Node.
1625 * @return {boolean} True if the second node exists and the first node is a 'd'. 1627 * @return {boolean} True if the second node exists and the first node is a 'd'.
1626 * @private 1628 * @private
1627 */ 1629 */
1628 cvox.SemanticTree.integralDxBoundary_ = function( 1630 cvox.SemanticTree.integralDxBoundary_ = function(firstNode, secondNode) {
1629 firstNode, secondNode) {
1630 return !!secondNode && 1631 return !!secondNode &&
1631 cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(secondNode) && 1632 cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(secondNode) &&
1632 cvox.SemanticAttr.isCharacterD(firstNode.textContent); 1633 cvox.SemanticAttr.isCharacterD(firstNode.textContent);
1633 }; 1634 };
1634 1635
1635 1636
1636 /** 1637 /**
1637 * Predicate implementing the boundary criteria for integrals dx on a single 1638 * Predicate implementing the boundary criteria for integrals dx on a single
1638 * node. 1639 * node.
1639 * @param {cvox.SemanticTree.Node} node A semantic node. 1640 * @param {cvox.SemanticTree.Node} node A semantic node.
1640 * @return {boolean} True if the node meets the boundary criteria. 1641 * @return {boolean} True if the node meets the boundary criteria.
1641 * @private 1642 * @private
1642 */ 1643 */
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
1697 }; 1698 };
1698 1699
1699 1700
1700 /** 1701 /**
1701 * Decides if a node is a table or multiline element. 1702 * Decides if a node is a table or multiline element.
1702 * @param {cvox.SemanticTree.Node} node A node. 1703 * @param {cvox.SemanticTree.Node} node A node.
1703 * @return {boolean} True if node is either table or multiline. 1704 * @return {boolean} True if node is either table or multiline.
1704 * @private 1705 * @private
1705 */ 1706 */
1706 cvox.SemanticTree.isTableOrMultiline_ = function(node) { 1707 cvox.SemanticTree.isTableOrMultiline_ = function(node) {
1707 return !!node && (cvox.SemanticTree.attrPred_('type', 'TABLE')(node) || 1708 return !!node &&
1708 cvox.SemanticTree.attrPred_('type', 'MULTILINE')(node)); 1709 (cvox.SemanticTree.attrPred_('type', 'TABLE')(node) ||
1710 cvox.SemanticTree.attrPred_('type', 'MULTILINE')(node));
1709 }; 1711 };
1710 1712
1711 1713
1712 /** 1714 /**
1713 * Heuristic to decide if we have a matrix: An expression fenced on both sides 1715 * Heuristic to decide if we have a matrix: An expression fenced on both sides
1714 * without any other content is considered a fenced node. 1716 * without any other content is considered a fenced node.
1715 * @param {cvox.SemanticTree.Node} node A node. 1717 * @param {cvox.SemanticTree.Node} node A node.
1716 * @return {boolean} True if we believe we have a matrix. 1718 * @return {boolean} True if we believe we have a matrix.
1717 * @private 1719 * @private
1718 */ 1720 */
1719 cvox.SemanticTree.tableIsMatrixOrVector_ = function(node) { 1721 cvox.SemanticTree.tableIsMatrixOrVector_ = function(node) {
1720 return !!node && cvox.SemanticTree.attrPred_('type', 'FENCED')(node) && 1722 return !!node && cvox.SemanticTree.attrPred_('type', 'FENCED')(node) &&
1721 cvox.SemanticTree.attrPred_('role', 'LEFTRIGHT')(node) && 1723 cvox.SemanticTree.attrPred_('role', 'LEFTRIGHT')(node) &&
1722 node.childNodes.length == 1 && 1724 node.childNodes.length == 1 &&
1723 cvox.SemanticTree.isTableOrMultiline_(node.childNodes[0]); 1725 cvox.SemanticTree.isTableOrMultiline_(node.childNodes[0]);
1724 }; 1726 };
1725 1727
1726 1728
1727 /** 1729 /**
1728 * Replaces a fenced node by a matrix or vector node. 1730 * Replaces a fenced node by a matrix or vector node.
1729 * @param {!cvox.SemanticTree.Node} node The fenced node to be rewritten. 1731 * @param {!cvox.SemanticTree.Node} node The fenced node to be rewritten.
1730 * @return {!cvox.SemanticTree.Node} The matrix or vector node. 1732 * @return {!cvox.SemanticTree.Node} The matrix or vector node.
1731 * @private 1733 * @private
1732 */ 1734 */
1733 cvox.SemanticTree.prototype.tableToMatrixOrVector_ = function(node) { 1735 cvox.SemanticTree.prototype.tableToMatrixOrVector_ = function(node) {
1734 var matrix = node.childNodes[0]; 1736 var matrix = node.childNodes[0];
1735 var type = cvox.SemanticTree.attrPred_('type', 'MULTILINE')(matrix) ? 1737 var type = cvox.SemanticTree.attrPred_('type', 'MULTILINE')(matrix) ?
1736 'VECTOR' : 'MATRIX'; 1738 'VECTOR' :
1739 'MATRIX';
1737 matrix.type = cvox.SemanticAttr.Type[type]; 1740 matrix.type = cvox.SemanticAttr.Type[type];
1738 node.contentNodes.forEach(goog.bind(matrix.appendContentNode_, matrix)); 1741 node.contentNodes.forEach(goog.bind(matrix.appendContentNode_, matrix));
1739 for (var i = 0, row; row = matrix.childNodes[i]; i++) { 1742 for (var i = 0, row; row = matrix.childNodes[i]; i++) {
1740 cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role[type]); 1743 cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role[type]);
1741 } 1744 }
1742 return matrix; 1745 return matrix;
1743 }; 1746 };
1744 1747
1745 1748
1746 /** 1749 /**
1747 * Heuristic to decide if we have a case statement: An expression with a 1750 * Heuristic to decide if we have a case statement: An expression with a
1748 * singular open fence before it. 1751 * singular open fence before it.
1749 * @param {!cvox.SemanticTree.Node} table A table node. 1752 * @param {!cvox.SemanticTree.Node} table A table node.
1750 * @param {!Array<cvox.SemanticTree.Node>} prevNodes A list of previous nodes. 1753 * @param {!Array<cvox.SemanticTree.Node>} prevNodes A list of previous nodes.
1751 * @return {boolean} True if we believe we have a case statement. 1754 * @return {boolean} True if we believe we have a case statement.
1752 * @private 1755 * @private
1753 */ 1756 */
1754 cvox.SemanticTree.tableIsCases_ = function(table, prevNodes) { 1757 cvox.SemanticTree.tableIsCases_ = function(table, prevNodes) {
1755 return prevNodes.length > 0 && 1758 return prevNodes.length > 0 &&
1756 cvox.SemanticTree.attrPred_('role', 'OPENFENCE')( 1759 cvox.SemanticTree.attrPred_(
1757 prevNodes[prevNodes.length - 1]); 1760 'role', 'OPENFENCE')(prevNodes[prevNodes.length - 1]);
1758 }; 1761 };
1759 1762
1760 1763
1761 /** 1764 /**
1762 * Makes case node out of a table and a fence. 1765 * Makes case node out of a table and a fence.
1763 * @param {!cvox.SemanticTree.Node} table The table containing the cases. 1766 * @param {!cvox.SemanticTree.Node} table The table containing the cases.
1764 * @param {!cvox.SemanticTree.Node} openFence The left delimiter of the case 1767 * @param {!cvox.SemanticTree.Node} openFence The left delimiter of the case
1765 * statement. 1768 * statement.
1766 * @return {!cvox.SemanticTree.Node} The cases node. 1769 * @return {!cvox.SemanticTree.Node} The cases node.
1767 * @private 1770 * @private
(...skipping 15 matching lines...) Expand all
1783 // similarities in columns (e.g., single relation symbols, like equalities or 1786 // similarities in columns (e.g., single relation symbols, like equalities or
1784 // inequalities in the same column could indicate an equation array). 1787 // inequalities in the same column could indicate an equation array).
1785 /** 1788 /**
1786 * Heuristic to decide if we have a multiline formula. A table is considered a 1789 * Heuristic to decide if we have a multiline formula. A table is considered a
1787 * multiline formula if it does not have any separate cells. 1790 * multiline formula if it does not have any separate cells.
1788 * @param {!cvox.SemanticTree.Node} table A table node. 1791 * @param {!cvox.SemanticTree.Node} table A table node.
1789 * @return {boolean} True if we believe we have a mulitline formula. 1792 * @return {boolean} True if we believe we have a mulitline formula.
1790 * @private 1793 * @private
1791 */ 1794 */
1792 cvox.SemanticTree.tableIsMultiline_ = function(table) { 1795 cvox.SemanticTree.tableIsMultiline_ = function(table) {
1793 return table.childNodes.every( 1796 return table.childNodes.every(function(row) {
1794 function(row) { 1797 var length = row.childNodes.length;
1795 var length = row.childNodes.length; 1798 return length <= 1;
1796 return length <= 1;}); 1799 });
1797 }; 1800 };
1798 1801
1799 1802
1800 /** 1803 /**
1801 * Rewrites a table to multiline structure, simplifying it by getting rid of the 1804 * Rewrites a table to multiline structure, simplifying it by getting rid of the
1802 * cell hierarchy level. 1805 * cell hierarchy level.
1803 * @param {!cvox.SemanticTree.Node} table The node to be rewritten a multiline. 1806 * @param {!cvox.SemanticTree.Node} table The node to be rewritten a multiline.
1804 * @private 1807 * @private
1805 */ 1808 */
1806 cvox.SemanticTree.prototype.tableToMultiline_ = function(table) { 1809 cvox.SemanticTree.prototype.tableToMultiline_ = function(table) {
1807 table.type = cvox.SemanticAttr.Type.MULTILINE; 1810 table.type = cvox.SemanticAttr.Type.MULTILINE;
1808 for (var i = 0, row; row = table.childNodes[i]; i++) { 1811 for (var i = 0, row; row = table.childNodes[i]; i++) {
1809 cvox.SemanticTree.rowToLine_(row, cvox.SemanticAttr.Role.MULTILINE); 1812 cvox.SemanticTree.rowToLine_(row, cvox.SemanticAttr.Role.MULTILINE);
1810 } 1813 }
1811 }; 1814 };
1812 1815
1813 1816
1814 /** 1817 /**
1815 * Converts a row that only contains one cell into a single line. 1818 * Converts a row that only contains one cell into a single line.
1816 * @param {!cvox.SemanticTree.Node} row The row to convert. 1819 * @param {!cvox.SemanticTree.Node} row The row to convert.
1817 * @param {cvox.SemanticAttr.Role=} role The new role for the line. 1820 * @param {cvox.SemanticAttr.Role=} role The new role for the line.
1818 * @private 1821 * @private
1819 */ 1822 */
1820 cvox.SemanticTree.rowToLine_ = function(row, role) { 1823 cvox.SemanticTree.rowToLine_ = function(row, role) {
1821 role = role || cvox.SemanticAttr.Role.UNKNOWN; 1824 role = role || cvox.SemanticAttr.Role.UNKNOWN;
1822 if (cvox.SemanticTree.attrPred_('type', 'ROW')(row) && 1825 if (cvox.SemanticTree.attrPred_('type', 'ROW')(row) &&
1823 row.childNodes.length == 1 && 1826 row.childNodes.length == 1 &&
1824 cvox.SemanticTree.attrPred_('type', 'CELL')(row.childNodes[0])) { 1827 cvox.SemanticTree.attrPred_('type', 'CELL')(row.childNodes[0])) {
1825 row.type = cvox.SemanticAttr.Type.LINE; 1828 row.type = cvox.SemanticAttr.Type.LINE;
1826 row.role = role; 1829 row.role = role;
1827 row.childNodes = row.childNodes[0].childNodes; 1830 row.childNodes = row.childNodes[0].childNodes;
1828 } 1831 }
1829 }; 1832 };
1830 1833
1831 1834
1832 /** 1835 /**
1833 * Assign a row and its contained cells a new role value. 1836 * Assign a row and its contained cells a new role value.
1834 * @param {!cvox.SemanticTree.Node} row The row to be updated. 1837 * @param {!cvox.SemanticTree.Node} row The row to be updated.
(...skipping 29 matching lines...) Expand all
1864 * @private 1867 * @private
1865 */ 1868 */
1866 cvox.SemanticTree.sliceNodes_ = function(nodes, pred, reverse) { 1869 cvox.SemanticTree.sliceNodes_ = function(nodes, pred, reverse) {
1867 if (reverse) { 1870 if (reverse) {
1868 nodes.reverse(); 1871 nodes.reverse();
1869 } 1872 }
1870 var head = []; 1873 var head = [];
1871 for (var i = 0, node; node = nodes[i]; i++) { 1874 for (var i = 0, node; node = nodes[i]; i++) {
1872 if (pred(node)) { 1875 if (pred(node)) {
1873 if (reverse) { 1876 if (reverse) {
1874 return {head: nodes.slice(i + 1).reverse(), 1877 return {
1875 div: node, 1878 head: nodes.slice(i + 1).reverse(),
1876 tail: head.reverse()}; 1879 div: node,
1880 tail: head.reverse()
1881 };
1877 } 1882 }
1878 return {head: head, 1883 return {head: head, div: node, tail: nodes.slice(i + 1)};
1879 div: node,
1880 tail: nodes.slice(i + 1)};
1881 } 1884 }
1882 head.push(node); 1885 head.push(node);
1883 } 1886 }
1884 if (reverse) { 1887 if (reverse) {
1885 return {head: [], div: null, tail: head.reverse()}; 1888 return {head: [], div: null, tail: head.reverse()};
1886 } 1889 }
1887 return {head: head, div: null, tail: []}; 1890 return {head: head, div: null, tail: []};
1888 }; 1891 };
1889 1892
1890 1893
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1924 * Constructs a predicate to check the semantic attribute of a node. 1927 * Constructs a predicate to check the semantic attribute of a node.
1925 * @param {!string} prop The property of a node. 1928 * @param {!string} prop The property of a node.
1926 * @param {!string} attr The attribute. 1929 * @param {!string} attr The attribute.
1927 * @return {function(cvox.SemanticTree.Node): boolean} The predicate. 1930 * @return {function(cvox.SemanticTree.Node): boolean} The predicate.
1928 * @private 1931 * @private
1929 */ 1932 */
1930 1933
1931 cvox.SemanticTree.attrPred_ = function(prop, attr) { 1934 cvox.SemanticTree.attrPred_ = function(prop, attr) {
1932 var getAttr = function(prop) { 1935 var getAttr = function(prop) {
1933 switch (prop) { 1936 switch (prop) {
1934 case 'type': return cvox.SemanticAttr.Type[attr]; 1937 case 'type':
1935 case 'role': return cvox.SemanticAttr.Role[attr]; 1938 return cvox.SemanticAttr.Type[attr];
1936 case 'font': return cvox.SemanticAttr.Font[attr]; 1939 case 'role':
1940 return cvox.SemanticAttr.Role[attr];
1941 case 'font':
1942 return cvox.SemanticAttr.Font[attr];
1937 } 1943 }
1938 }; 1944 };
1939 1945
1940 return function(node) {return node[prop] == getAttr(prop);}; 1946 return function(node) {
1947 return node[prop] == getAttr(prop);
1948 };
1941 }; 1949 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698