Files
2025-07-16 16:30:56 +00:00

195 lines
8.1 KiB
JavaScript

import { Attribute } from '../enrich_mathml/enrich_attr.js';
import { NamedSymbol } from '../semantic_tree/semantic_attr.js';
import { SemanticFont, SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { SemanticNodeFactory } from '../semantic_tree/semantic_node_factory.js';
import { SemanticSkeleton } from '../semantic_tree/semantic_skeleton.js';
import { SemanticTree } from '../semantic_tree/semantic_tree.js';
import * as WalkerUtil from './walker_util.js';
export class RebuildStree {
static textContent(snode, node, ignore) {
if (!ignore && node.textContent) {
snode.textContent = node.textContent;
return;
}
const operator = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(node, Attribute.OPERATOR));
if (operator.length > 1) {
snode.textContent = operator[1];
}
}
static isPunctuated(collapsed) {
return (!SemanticSkeleton.simpleCollapseStructure(collapsed) &&
collapsed[1] &&
SemanticSkeleton.contentCollapseStructure(collapsed[1]));
}
constructor(mathml) {
this.mathml = mathml;
this.factory = new SemanticNodeFactory();
this.nodeDict = {};
this.mmlRoot = WalkerUtil.getSemanticRoot(mathml);
this.streeRoot = this.assembleTree(this.mmlRoot);
this.stree = SemanticTree.fromNode(this.streeRoot, this.mathml);
this.xml = this.stree.xml();
}
getTree() {
return this.stree;
}
assembleTree(node) {
const snode = this.makeNode(node);
const children = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(node, Attribute.CHILDREN));
const content = WalkerUtil.splitAttribute(WalkerUtil.getAttribute(node, Attribute.CONTENT));
if (content.length === 0 && children.length === 0) {
RebuildStree.textContent(snode, node);
return snode;
}
if (content.length > 0) {
const fcontent = WalkerUtil.getBySemanticId(this.mathml, content[0]);
if (fcontent) {
RebuildStree.textContent(snode, fcontent, true);
}
}
snode.contentNodes = content.map((id) => this.setParent(id, snode));
snode.childNodes = children.map((id) => this.setParent(id, snode));
const collapsed = WalkerUtil.getAttribute(node, Attribute.COLLAPSED);
return collapsed ? this.postProcess(snode, collapsed) : snode;
}
makeNode(node) {
const type = WalkerUtil.getAttribute(node, Attribute.TYPE);
const role = WalkerUtil.getAttribute(node, Attribute.ROLE);
const font = WalkerUtil.getAttribute(node, Attribute.FONT);
const annotation = WalkerUtil.getAttribute(node, Attribute.ANNOTATION) || '';
const attributes = WalkerUtil.getAttribute(node, Attribute.ATTRIBUTES) || '';
const id = WalkerUtil.getAttribute(node, Attribute.ID);
const embellished = WalkerUtil.getAttribute(node, Attribute.EMBELLISHED);
const fencepointer = WalkerUtil.getAttribute(node, Attribute.FENCEPOINTER);
const snode = this.createNode(parseInt(id, 10));
snode.type = type;
snode.role = role;
snode.font = font ? font : SemanticFont.UNKNOWN;
snode.parseAnnotation(annotation);
snode.parseAttributes(attributes);
if (fencepointer) {
snode.fencePointer = fencepointer;
}
if (embellished) {
snode.embellished = embellished;
}
return snode;
}
makePunctuation(id) {
const node = this.createNode(id);
node.updateContent(NamedSymbol.invisibleComma);
node.role = SemanticRole.DUMMY;
return node;
}
makePunctuated(snode, collapsed, role) {
const punctuated = this.createNode(collapsed[0]);
punctuated.type = SemanticType.PUNCTUATED;
punctuated.embellished = snode.embellished;
punctuated.fencePointer = snode.fencePointer;
punctuated.role = role;
const cont = collapsed.splice(1, 1)[0].slice(1);
punctuated.contentNodes = cont.map(this.makePunctuation.bind(this));
this.collapsedChildren_(collapsed);
}
makeEmpty(snode, collapsed, role) {
const empty = this.createNode(collapsed);
empty.type = SemanticType.EMPTY;
empty.embellished = snode.embellished;
empty.fencePointer = snode.fencePointer;
empty.role = role;
}
makeIndex(snode, collapsed, role) {
if (RebuildStree.isPunctuated(collapsed)) {
this.makePunctuated(snode, collapsed, role);
collapsed = collapsed[0];
return;
}
if (SemanticSkeleton.simpleCollapseStructure(collapsed) &&
!this.nodeDict[collapsed.toString()]) {
this.makeEmpty(snode, collapsed, role);
}
}
postProcess(snode, collapsed) {
const array = SemanticSkeleton.fromString(collapsed).array;
if (snode.type === SemanticRole.SUBSUP) {
const subscript = this.createNode(array[1][0]);
subscript.type = SemanticType.SUBSCRIPT;
subscript.role = SemanticRole.SUBSUP;
snode.type = SemanticType.SUPERSCRIPT;
subscript.embellished = snode.embellished;
subscript.fencePointer = snode.fencePointer;
this.makeIndex(snode, array[1][2], SemanticRole.RIGHTSUB);
this.makeIndex(snode, array[2], SemanticRole.RIGHTSUPER);
this.collapsedChildren_(array);
return snode;
}
if (snode.type === SemanticType.SUBSCRIPT) {
this.makeIndex(snode, array[2], SemanticRole.RIGHTSUB);
this.collapsedChildren_(array);
return snode;
}
if (snode.type === SemanticType.SUPERSCRIPT) {
this.makeIndex(snode, array[2], SemanticRole.RIGHTSUPER);
this.collapsedChildren_(array);
return snode;
}
if (snode.type === SemanticType.TENSOR) {
this.makeIndex(snode, array[2], SemanticRole.LEFTSUB);
this.makeIndex(snode, array[3], SemanticRole.LEFTSUPER);
this.makeIndex(snode, array[4], SemanticRole.RIGHTSUB);
this.makeIndex(snode, array[5], SemanticRole.RIGHTSUPER);
this.collapsedChildren_(array);
return snode;
}
if (snode.type === SemanticType.PUNCTUATED) {
if (RebuildStree.isPunctuated(array)) {
const cont = array.splice(1, 1)[0].slice(1);
snode.contentNodes = cont.map(this.makePunctuation.bind(this));
}
return snode;
}
if (snode.type === SemanticRole.UNDEROVER) {
const score = this.createNode(array[1][0]);
if (snode.childNodes[1].role === SemanticRole.OVERACCENT) {
score.type = SemanticType.OVERSCORE;
snode.type = SemanticType.UNDERSCORE;
}
else {
score.type = SemanticType.UNDERSCORE;
snode.type = SemanticType.OVERSCORE;
}
score.role = SemanticRole.UNDEROVER;
score.embellished = snode.embellished;
score.fencePointer = snode.fencePointer;
this.collapsedChildren_(array);
return snode;
}
return snode;
}
createNode(id) {
const node = this.factory.makeNode(id);
this.nodeDict[id.toString()] = node;
return node;
}
collapsedChildren_(collapsed) {
const recurseCollapsed = (coll) => {
const parent = this.nodeDict[coll[0]];
parent.childNodes = [];
for (let j = 1, l = coll.length; j < l; j++) {
const id = coll[j];
parent.childNodes.push(SemanticSkeleton.simpleCollapseStructure(id)
? this.nodeDict[id]
: recurseCollapsed(id));
}
return parent;
};
recurseCollapsed(collapsed);
}
setParent(id, snode) {
const mml = WalkerUtil.getBySemanticId(this.mathml, id);
const sn = this.assembleTree(mml);
sn.parent = snode;
return sn;
}
}