UNPKG

mathjslab

Version:

MathJSLab - An interpreter with language syntax like MATLAB®/Octave, ISBN 978-65-00-82338-7.

213 lines (197 loc) 7.35 kB
import { ComplexDecimal } from './ComplexDecimal'; import { Evaluator } from './Evaluator'; import { type ElementType, MultiArray } from './MultiArray'; class Structure { public static readonly STRUCTURE = 4; public readonly type = Structure.STRUCTURE; parent: any; public field: Record<string, ElementType>; private static readonly invalidReferenceMessage = 'value cannot be indexed with .'; /** * Structure constructor. If an object is passed as parameter then create * a Structure with same fields and values of object. If an array of field * names as string is passed then create a Structure with this field * branch and nested field value set to empty array. * @param field An object with fields and values or an array of field names. */ constructor(field: Record<string, ElementType> | string[]) { this.field = {}; if (Array.isArray(field)) { let struct = this as Structure; for (let i = 0; i < field.length - 1; i++) { struct.field[field[i]] = new Structure({}); struct = struct.field[field[i]] as Structure; } struct.field[field[field.length - 1]] = MultiArray.emptyArray(); } else { for (const f in field) { this.field[f] = field[f]!.copy(); } } } /** * Return true if obj is a structure or a structure array. * @param obj * @returns */ public static isStructure(obj: ElementType): boolean { return obj instanceof Structure || (obj instanceof MultiArray && !obj.isCell && obj.dimension[0] > 0 && obj.dimension[1] > 0 && obj.array[0][0] instanceof Structure); } /** * * @param S * @param field * @param value */ public static setField(S: Structure, field: string[], value?: ElementType): void { // TODO: check if struct.field[field[i]] exists, if it is a MultiArray of Structure... let struct = S; for (let i = 0; i < field.length - 1; i++) { struct.field[field[i]] = new Structure({}); struct = struct.field[field[i]] as Structure; } struct.field[field[field.length - 1]] = value ?? MultiArray.emptyArray(); } /** * * @param S * @param field * @param value */ public static setNewField(S: Structure, field: string[], value?: ElementType): void { let struct = S; for (let i = 0; i < field.length - 1; i++) { if (!(struct.field[field[i]] instanceof Structure)) { if (typeof struct.field[field[i]] === 'undefined' || MultiArray.isEmpty(struct.field[field[i]])) { struct.field[field[i]] = new Structure({}); } else { throw new EvalError(Structure.invalidReferenceMessage); } } struct = struct.field[field[i]] as Structure; } struct.field[field[field.length - 1]] = value ?? MultiArray.emptyArray(); } /** * * @param obj * @param field * @returns */ public static getField(obj: ElementType, field: string[]): ElementType { if (obj instanceof Structure) { let struct = obj; let i; for (i = 0; i < field.length - 1; i++) { if (struct instanceof Structure && typeof struct.field[field[i]] !== 'undefined') { struct = struct.field[field[i]] as Structure; } else { break; } } if (i === field.length - 1 && struct instanceof Structure && typeof struct.field[field[field.length - 1]] !== 'undefined') { return struct.field[field[field.length - 1]]; } else { throw new EvalError(Structure.invalidReferenceMessage); } } else { throw new EvalError(Structure.invalidReferenceMessage); } } public static getFields(obj: ElementType, field: string[]): ElementType[] { return obj instanceof MultiArray && obj.array.length > 0 && obj.array[0].length > 0 && obj.array[0][0] instanceof Structure ? MultiArray.linearize(obj).map((S) => Structure.getField(S, field)) : [Structure.getField(obj, field)]; } /** * * @param S * @param evaluator * @returns */ public static unparse(S: Structure, evaluator: Evaluator, parentPrecedence = 0): string { return `struct {\n${Object.entries(S.field) .map((entry) => `${entry[0]}: ${evaluator.Unparse(entry[1])}`) .join('\n')}\n}`; } /** * * @param S * @param evaluator * @returns */ public static unparseMathML(S: Structure, evaluator: Evaluator, parentPrecedence = 0): string { let result = `<mtr><mtd columnspan="2"><mtext>struct {</mtext></mtd></mtr>`; result += Object.entries(S.field) .map((entry) => `<mtr><mtd><mi>${entry[0]}</mi><mo>:</mo></mtd><mtd>${evaluator.unparserMathML(entry[1])}</mtd></mtr>`) .join(''); result += `<mtr><mtd columnspan="2"><mtext>}</mtext></mtd></mtr>`; return `<mtable>${result}</mtable>`; } /** * * @param S * @returns */ public static copy(S: Structure): Structure { const result = new Structure({}); for (const f in S.field) { result.field[f] = S.field[f]!.copy(); } return result; } /** * * @returns */ public copy(): Structure { return Structure.copy(this); } /** * * @param S * @returns */ public static cloneFields(S: Structure): Structure { const result = new Structure({}); Object.keys(S.field).forEach((key) => { result.field[key] = MultiArray.emptyArray(); }); return result; } /** * * @param S * @returns */ public static toLogical(S: Structure): ComplexDecimal { return Object.keys(S.field).length > 0 ? ComplexDecimal.true() : ComplexDecimal.false(); } /** * * @returns */ public toLogical(): ComplexDecimal { return Object.keys(this.field).length > 0 ? ComplexDecimal.true() : ComplexDecimal.false(); } /** * Set empty field in all elements of MultiArray if it is not cell array and if field not defined. * @param M * @param field */ public static setEmptyField(M: MultiArray, field: string): void { if (M.array[0][0] instanceof Structure) { if (!(M.isCell || field in M.array[0][0].field)) { for (let i = 0; i < M.array.length; i++) { for (let j = 0; j < M.dimension[1]; j++) { (M.array[i][j] as Structure).field[field] = MultiArray.emptyArray(); } } } } else { throw new EvalError(Structure.invalidReferenceMessage); } } } export { Structure }; export default Structure;