OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 }; |
OLD | NEW |