Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix problems with verification and repair of malformed mtables. #779

Merged
merged 4 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions ts/core/MmlTree/MmlNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,9 @@ export interface MmlNode extends Node {
* @param {string} message The error message to use
* @param {PropertyList} options The options telling how much to verify
* @param {boolean} short True means use just the kind if not using full errors
* @return {MmlNode} The construted merror
*/
mError(message: string, options: PropertyList, short?: boolean): void;
mError(message: string, options: PropertyList, short?: boolean): MmlNode;

/**
* Check integrity of MathML structure
Expand Down Expand Up @@ -797,12 +798,14 @@ export abstract class AbstractMmlNode extends AbstractNode implements MmlNode {
* @param {string} message The error message to use
* @param {PropertyList} options The options telling how much to verify
* @param {boolean} short True means use just the kind if not using full errors
* @return {MmlNode} The constructed merror
*/
public mError(message: string, options: PropertyList, short: boolean = false) {
public mError(message: string, options: PropertyList, short: boolean = false): MmlNode {
if (this.parent && this.parent.isKind('merror')) {
return null;
}
let merror = this.factory.create('merror');
merror.attributes.set('data-mjx-message', message);
if (options['fullErrors'] || short) {
let mtext = this.factory.create('mtext');
let text = this.factory.create('text') as TextNode;
Expand Down Expand Up @@ -1189,7 +1192,9 @@ export abstract class AbstractMmlEmptyNode extends AbstractEmptyNode implements
/**
* @override
*/
public mError(_message: string, _options: PropertyList, _short: boolean = false) {}
public mError(_message: string, _options: PropertyList, _short: boolean = false) {
return null as MmlNode;
}

}

Expand Down
29 changes: 25 additions & 4 deletions ts/core/MmlTree/MmlNodes/mtable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,31 @@ export class MmlMtable extends AbstractMmlNode {
* @override
*/
protected verifyChildren(options: PropertyList) {
if (!options['fixMtables']) {
for (const child of this.childNodes) {
if (!child.isKind('mtr')) {
this.mError('Children of ' + this.kind + ' must be mtr or mlabeledtr', options);
let mtr: MmlNode = null; // all consecutive non-mtr elements are collected into one mtr
const factory = this.factory;
for (let i = 0; i < this.childNodes.length; i++) {
const child = this.childNodes[i];
if (child.isKind('mtr')) {
mtr = null; // start a new row if there are non-mtr children
} else {
const isMtd = child.isKind('mtd');
//
// If there is already an mtr for previous children, just remove the child
// otherwise repalce the child with a new mtr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

//
if (mtr) {
this.removeChild(child);
i--; // there is one fewer child now
} else {
mtr = this.replaceChild(factory.create('mtr'), child) as MmlNode;
}
mtr.appendChild(isMtd ? child : factory.create('mtd', {}, [child])); // Move the child into the mtr
if (!options['fixMtables']) {
child.parent.removeChild(child); // remove the child from its mtd or mtr
child.parent = this; // ... and make it think it is a child of the table again
isMtd && mtr.appendChild(factory.create('mtd')); // child will be replaced, so make sure there is an mtd
const merror = child.mError('Children of ' + this.kind + ' must be mtr or mlabeledtr', options, isMtd);
mtr.childNodes[mtr.childNodes.length - 1].appendChild(merror); // append the error to the mtd in the mtr
}
}
}
Expand Down
11 changes: 6 additions & 5 deletions ts/core/MmlTree/MmlNodes/mtr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,12 @@ export class MmlMtr extends AbstractMmlNode {
this.mError(this.kind + ' can only be a child of an mtable', options, true);
return;
}
if (!options['fixMtables']) {
for (const child of this.childNodes) {
if (!child.isKind('mtd')) {
let mtr = this.replaceChild(this.factory.create('mtr'), child) as MmlNode;
mtr.mError('Children of ' + this.kind + ' must be mtd', options, true);
for (const child of this.childNodes) {
if (!child.isKind('mtd')) {
let mtd = this.replaceChild(this.factory.create('mtd'), child) as MmlNode;
mtd.appendChild(child);
if (!options['fixMtables']) {
child.mError('Children of ' + this.kind + ' must be mtd', options);
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions ts/core/Tree/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ export interface Node {
*/
replaceChild(newChild: Node, oldChild: Node): Node;

/**
* @param {Node} child Child node to be removed
* @return {Node} The old child node that was removed
*/
removeChild(child: Node): Node;

/**
* @param {Node} child A child node whose index in childNodes is desired
* @return {number} The index of the child in childNodes, or null if not found
Expand Down Expand Up @@ -255,10 +261,23 @@ export abstract class AbstractNode implements Node {
if (i !== null) {
this.childNodes[i] = newChild;
newChild.parent = this;
oldChild.parent = null;
}
return newChild;
}

/**
* @override
*/
public removeChild(child: Node) {
const i = this.childIndex(child);
if (i !== null) {
this.childNodes.splice(i, 1);
child.parent = null;
}
return child;
}


/**
* @override
Expand Down