cellml-text-editor 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
var n = /* @__PURE__ */ ((
|
|
2
|
-
class
|
|
1
|
+
var n = /* @__PURE__ */ ((a) => (a[a.Unknown = 0] = "Unknown", a[a.EOF = 1] = "EOF", a[a.Identifier = 2] = "Identifier", a[a.Number = 3] = "Number", a[a.String = 4] = "String", a[a.KwDef = 5] = "KwDef", a[a.KwModel = 6] = "KwModel", a[a.KwComp = 7] = "KwComp", a[a.KwEndDef = 8] = "KwEndDef", a[a.KwAs = 9] = "KwAs", a[a.KwVar = 10] = "KwVar", a[a.KwUnit = 11] = "KwUnit", a[a.KwSel = 12] = "KwSel", a[a.KwCase = 13] = "KwCase", a[a.KwOtherwise = 14] = "KwOtherwise", a[a.KwEndSel = 15] = "KwEndSel", a[a.OpAss = 16] = "OpAss", a[a.OpPlus = 17] = "OpPlus", a[a.OpMinus = 18] = "OpMinus", a[a.OpTimes = 19] = "OpTimes", a[a.OpDivide = 20] = "OpDivide", a[a.OpComma = 21] = "OpComma", a[a.Colon = 22] = "Colon", a[a.SemiColon = 23] = "SemiColon", a[a.LParam = 24] = "LParam", a[a.RParam = 25] = "RParam", a[a.LBrace = 26] = "LBrace", a[a.RBrace = 27] = "RBrace", a[a.OpEq = 28] = "OpEq", a[a.OpNe = 29] = "OpNe", a[a.OpLt = 30] = "OpLt", a[a.OpLe = 31] = "OpLe", a[a.OpGt = 32] = "OpGt", a[a.OpGe = 33] = "OpGe", a[a.OpAnd = 34] = "OpAnd", a[a.OpOr = 35] = "OpOr", a))(n || {});
|
|
2
|
+
class x {
|
|
3
3
|
input;
|
|
4
4
|
pos = 0;
|
|
5
5
|
line = 1;
|
|
6
6
|
length;
|
|
7
7
|
currentToken = 1;
|
|
8
8
|
currentValue = "";
|
|
9
|
-
constructor(
|
|
10
|
-
this.input =
|
|
9
|
+
constructor(e) {
|
|
10
|
+
this.input = e, this.length = e.length, this.nextToken();
|
|
11
11
|
}
|
|
12
12
|
get token() {
|
|
13
13
|
return this.currentToken;
|
|
@@ -17,28 +17,28 @@ class $ {
|
|
|
17
17
|
}
|
|
18
18
|
// Advance to the next token
|
|
19
19
|
nextToken() {
|
|
20
|
-
const
|
|
21
|
-
if (this.line +=
|
|
20
|
+
const e = this.skipWhitespace();
|
|
21
|
+
if (this.line += e, this.pos >= this.length) {
|
|
22
22
|
this.currentToken = 1;
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
const s = this.input[this.pos];
|
|
26
26
|
if (/[a-zA-Z_]/.test(s || "")) {
|
|
27
|
-
let
|
|
27
|
+
let i = this.pos;
|
|
28
28
|
for (; this.pos < this.length && /[a-zA-Z0-9_]/.test(this.input[this.pos] || ""); )
|
|
29
29
|
this.pos++;
|
|
30
|
-
const
|
|
31
|
-
this.currentValue =
|
|
30
|
+
const t = this.input.slice(i, this.pos);
|
|
31
|
+
this.currentValue = t, this.currentToken = this.getKeywordType(t);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
if (/[0-9]/.test(s || "") || s === "." && /[0-9]/.test(this.input[this.pos + 1] || "")) {
|
|
35
|
-
let
|
|
35
|
+
let i = this.pos;
|
|
36
36
|
for (; this.pos < this.length && /[0-9]/.test(this.input[this.pos] || ""); ) this.pos++;
|
|
37
37
|
if (this.input[this.pos] === ".")
|
|
38
38
|
for (this.pos++; this.pos < this.length && /[0-9]/.test(this.input[this.pos] || ""); ) this.pos++;
|
|
39
39
|
if (this.input[this.pos] === "e" || this.input[this.pos] === "E")
|
|
40
40
|
for (this.pos++, (this.input[this.pos] === "+" || this.input[this.pos] === "-") && this.pos++; this.pos < this.length && /[0-9]/.test(this.input[this.pos] || ""); ) this.pos++;
|
|
41
|
-
this.currentValue = this.input.slice(
|
|
41
|
+
this.currentValue = this.input.slice(i, this.pos), this.currentToken = 3;
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
switch (this.pos++, this.currentValue = s || "", s) {
|
|
@@ -97,22 +97,22 @@ class $ {
|
|
|
97
97
|
return this.line;
|
|
98
98
|
}
|
|
99
99
|
skipWhitespace() {
|
|
100
|
-
let
|
|
100
|
+
let e = 0;
|
|
101
101
|
for (; this.pos < this.length; ) {
|
|
102
102
|
const s = this.input[this.pos];
|
|
103
103
|
if (/\s/.test(s || ""))
|
|
104
104
|
s === `
|
|
105
|
-
` &&
|
|
105
|
+
` && e++, this.pos++;
|
|
106
106
|
else if (s === "/" && this.input[this.pos + 1] === "/")
|
|
107
107
|
for (this.pos += 2; this.pos < this.length && this.input[this.pos] !== `
|
|
108
108
|
`; ) this.pos++;
|
|
109
109
|
else
|
|
110
110
|
break;
|
|
111
111
|
}
|
|
112
|
-
return
|
|
112
|
+
return e;
|
|
113
113
|
}
|
|
114
|
-
getKeywordType(
|
|
115
|
-
switch (
|
|
114
|
+
getKeywordType(e) {
|
|
115
|
+
switch (e) {
|
|
116
116
|
case "def":
|
|
117
117
|
return 5;
|
|
118
118
|
case "model":
|
|
@@ -149,11 +149,11 @@ class g {
|
|
|
149
149
|
scanner;
|
|
150
150
|
doc;
|
|
151
151
|
sourceLineAttr;
|
|
152
|
-
constructor(
|
|
153
|
-
this.sourceLineAttr =
|
|
152
|
+
constructor(e = {}) {
|
|
153
|
+
this.sourceLineAttr = e.sourceLineAttribute === void 0 ? "data-source-location" : e.sourceLineAttribute;
|
|
154
154
|
}
|
|
155
|
-
parse(
|
|
156
|
-
this.scanner = new
|
|
155
|
+
parse(e) {
|
|
156
|
+
this.scanner = new x(e), this.doc = document.implementation.createDocument(m, "model", null);
|
|
157
157
|
try {
|
|
158
158
|
const s = this.doc.documentElement;
|
|
159
159
|
for (this.expect(n.KwDef), this.expect(n.KwModel), this.scanner.token === n.Identifier && (s.setAttribute("name", this.scanner.value), this.scanner.nextToken()), this.expect(n.KwAs); this.scanner.token !== n.KwEndDef && this.scanner.token !== n.EOF; )
|
|
@@ -164,145 +164,183 @@ class g {
|
|
|
164
164
|
return { xml: null, errors: [{ line: this.scanner.getLine(), message: s.message || "Unknown parsing error" }] };
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
parseBlock(
|
|
167
|
+
parseBlock(e) {
|
|
168
168
|
if (this.expect(n.KwDef), this.scanner.token === n.KwComp)
|
|
169
|
-
this.parseComponent(
|
|
169
|
+
this.parseComponent(e);
|
|
170
170
|
else if (this.scanner.token === n.KwUnit)
|
|
171
|
-
this.parseUnit(
|
|
171
|
+
this.parseUnit(e);
|
|
172
172
|
else
|
|
173
173
|
throw new Error("Expected 'comp' or 'unit' after 'def'");
|
|
174
174
|
}
|
|
175
|
-
parseComponent(
|
|
175
|
+
parseComponent(e) {
|
|
176
176
|
this.expect(n.KwComp);
|
|
177
177
|
const s = this.expectValue(n.Identifier);
|
|
178
178
|
this.expect(n.KwAs);
|
|
179
|
-
const
|
|
180
|
-
for (
|
|
181
|
-
this.scanner.token === n.KwVar ? this.parseVariable(
|
|
179
|
+
const i = this.doc.createElementNS(m, "component");
|
|
180
|
+
for (i.setAttribute("name", s), e.appendChild(i); this.scanner.token !== n.KwEndDef && this.scanner.token !== n.EOF; )
|
|
181
|
+
this.scanner.token === n.KwVar ? this.parseVariable(i) : this.scanner.token === n.Identifier || this.scanner.token === n.KwSel ? this.parseMathEquation(i) : this.scanner.nextToken();
|
|
182
182
|
this.expect(n.KwEndDef), this.expect(n.SemiColon);
|
|
183
183
|
}
|
|
184
184
|
// var V: millivolt {init: -65, interface: public};
|
|
185
|
-
parseVariable(
|
|
185
|
+
parseVariable(e) {
|
|
186
186
|
this.expect(n.KwVar);
|
|
187
187
|
const s = this.expectValue(n.Identifier);
|
|
188
188
|
this.expect(n.Colon);
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
189
|
+
const i = this.expectValue(n.Identifier), t = this.doc.createElementNS(m, "variable");
|
|
190
|
+
if (t.setAttribute("name", s), t.setAttribute("units", i), this.scanner.token === n.LBrace) {
|
|
191
191
|
for (this.scanner.nextToken(); this.scanner.token !== n.RBrace && this.scanner.token !== n.EOF; ) {
|
|
192
|
-
const
|
|
192
|
+
const r = this.expectValue(n.Identifier);
|
|
193
193
|
this.expect(n.Colon);
|
|
194
194
|
let c = "";
|
|
195
|
-
this.scanner.token === n.OpMinus ? (this.scanner.nextToken(), c = "-" + this.expectValue(n.Number)) : this.scanner.token === n.Number ? c = this.expectValue(n.Number) : c = this.expectValue(n.Identifier),
|
|
195
|
+
this.scanner.token === n.OpMinus ? (this.scanner.nextToken(), c = "-" + this.expectValue(n.Number)) : this.scanner.token === n.Number ? c = this.expectValue(n.Number) : c = this.expectValue(n.Identifier), r === "init" ? t.setAttribute("initial_value", c) : r === "interface" && t.setAttribute("interface", c), this.scanner.token === n.OpComma && this.scanner.nextToken();
|
|
196
196
|
}
|
|
197
197
|
this.expect(n.RBrace);
|
|
198
198
|
}
|
|
199
|
-
this.expect(n.SemiColon),
|
|
199
|
+
this.expect(n.SemiColon), e.appendChild(t);
|
|
200
200
|
}
|
|
201
|
-
parseUnit(
|
|
201
|
+
parseUnit(e) {
|
|
202
202
|
for (this.expect(n.KwUnit); this.scanner.token !== n.KwEndDef && this.scanner.token !== n.EOF; )
|
|
203
203
|
this.scanner.nextToken();
|
|
204
204
|
this.expect(n.KwEndDef), this.expect(n.SemiColon);
|
|
205
205
|
}
|
|
206
206
|
// --- Math Parsing ---
|
|
207
|
-
parseMathEquation(
|
|
207
|
+
parseMathEquation(e) {
|
|
208
208
|
const s = this.scanner.getLine();
|
|
209
|
-
let
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
209
|
+
let i = e.getElementsByTagNameNS(h, "math")[0];
|
|
210
|
+
i || (i = this.doc.createElementNS(h, "math"), e.appendChild(i));
|
|
211
|
+
const t = this.doc.createElementNS(h, "apply"), r = this.doc.createElementNS(h, "eq");
|
|
212
|
+
t.appendChild(r);
|
|
213
213
|
const c = this.parseExpression();
|
|
214
214
|
this.expect(n.OpAss);
|
|
215
215
|
const o = this.parseExpression(), u = this.scanner.getLine();
|
|
216
|
-
this.sourceLineAttr &&
|
|
216
|
+
this.sourceLineAttr && t.setAttribute(
|
|
217
217
|
this.sourceLineAttr,
|
|
218
218
|
`${s.toString()}` + (u !== s ? `-${u.toString()}` : "")
|
|
219
|
-
),
|
|
219
|
+
), t.appendChild(c), t.appendChild(o), i.appendChild(t), this.expect(n.SemiColon);
|
|
220
220
|
}
|
|
221
221
|
// Recursive Descent for Math: Condition -> Comparison -> Expression -> Term -> Factor
|
|
222
222
|
parseCondition() {
|
|
223
|
-
let
|
|
223
|
+
let e = this.parseComparison();
|
|
224
224
|
for (; this.scanner.token === n.OpAnd || this.scanner.token === n.OpOr; ) {
|
|
225
225
|
const s = this.scanner.token;
|
|
226
226
|
this.scanner.nextToken();
|
|
227
|
-
const
|
|
228
|
-
|
|
227
|
+
const i = this.parseComparison(), t = this.doc.createElementNS(h, "apply"), r = this.doc.createElementNS(h, s === n.OpAnd ? "and" : "or");
|
|
228
|
+
t.appendChild(r), t.appendChild(e), t.appendChild(i), e = t;
|
|
229
229
|
}
|
|
230
|
-
return
|
|
230
|
+
return e;
|
|
231
231
|
}
|
|
232
|
-
isComparisonToken(
|
|
233
|
-
return [n.OpEq, n.OpNe, n.OpLt, n.OpLe, n.OpGt, n.OpGe].includes(
|
|
232
|
+
isComparisonToken(e) {
|
|
233
|
+
return [n.OpEq, n.OpNe, n.OpLt, n.OpLe, n.OpGt, n.OpGe].includes(e);
|
|
234
234
|
}
|
|
235
235
|
// The new parsing layer
|
|
236
236
|
parseComparison() {
|
|
237
|
-
let
|
|
237
|
+
let e = this.parseExpression();
|
|
238
238
|
if (this.isComparisonToken(this.scanner.token)) {
|
|
239
239
|
const s = this.scanner.token;
|
|
240
240
|
this.scanner.nextToken();
|
|
241
|
-
const
|
|
242
|
-
let
|
|
241
|
+
const i = this.parseExpression(), t = this.doc.createElementNS(h, "apply");
|
|
242
|
+
let r = "";
|
|
243
243
|
switch (s) {
|
|
244
244
|
case n.OpEq:
|
|
245
|
-
|
|
245
|
+
r = "eq";
|
|
246
246
|
break;
|
|
247
247
|
case n.OpNe:
|
|
248
|
-
|
|
248
|
+
r = "neq";
|
|
249
249
|
break;
|
|
250
250
|
case n.OpLt:
|
|
251
|
-
|
|
251
|
+
r = "lt";
|
|
252
252
|
break;
|
|
253
253
|
case n.OpLe:
|
|
254
|
-
|
|
254
|
+
r = "leq";
|
|
255
255
|
break;
|
|
256
256
|
case n.OpGt:
|
|
257
|
-
|
|
257
|
+
r = "gt";
|
|
258
258
|
break;
|
|
259
259
|
case n.OpGe:
|
|
260
|
-
|
|
260
|
+
r = "geq";
|
|
261
261
|
break;
|
|
262
262
|
case n.OpAnd:
|
|
263
|
-
|
|
263
|
+
r = "and";
|
|
264
264
|
break;
|
|
265
265
|
}
|
|
266
|
-
const c = this.doc.createElementNS(h,
|
|
267
|
-
return
|
|
266
|
+
const c = this.doc.createElementNS(h, r);
|
|
267
|
+
return t.appendChild(c), t.appendChild(e), t.appendChild(i), t;
|
|
268
268
|
}
|
|
269
|
-
return
|
|
269
|
+
return e;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Checks if an element is an <apply> block for a specific operator.
|
|
273
|
+
* e.g. isMathMLApply(node, 'plus') returns true for <apply><plus/>...</apply>
|
|
274
|
+
*/
|
|
275
|
+
isMathMLApply(e, s) {
|
|
276
|
+
if (e.localName !== "apply") return !1;
|
|
277
|
+
const i = e.firstElementChild;
|
|
278
|
+
return i ? i.localName === s : !1;
|
|
270
279
|
}
|
|
271
280
|
parseExpression() {
|
|
272
|
-
let
|
|
281
|
+
let e = this.parseTerm();
|
|
273
282
|
for (; this.scanner.token === n.OpPlus || this.scanner.token === n.OpMinus; ) {
|
|
274
283
|
const s = this.scanner.token;
|
|
275
284
|
this.scanner.nextToken();
|
|
276
|
-
const
|
|
277
|
-
|
|
285
|
+
const i = this.parseTerm();
|
|
286
|
+
if (s === n.OpPlus && this.isMathMLApply(e, "plus"))
|
|
287
|
+
e.appendChild(i);
|
|
288
|
+
else {
|
|
289
|
+
const t = this.doc.createElementNS(h, "apply"), r = this.doc.createElementNS(h, s === n.OpPlus ? "plus" : "minus");
|
|
290
|
+
t.appendChild(r), t.appendChild(e), t.appendChild(i), e = t;
|
|
291
|
+
}
|
|
278
292
|
}
|
|
279
|
-
return
|
|
293
|
+
return e;
|
|
280
294
|
}
|
|
281
295
|
parseTerm() {
|
|
282
|
-
let
|
|
296
|
+
let e = this.parseFactor();
|
|
283
297
|
for (; this.scanner.token === n.OpTimes || this.scanner.token === n.OpDivide; ) {
|
|
284
298
|
const s = this.scanner.token;
|
|
285
299
|
this.scanner.nextToken();
|
|
286
|
-
const
|
|
287
|
-
|
|
300
|
+
const i = this.parseFactor();
|
|
301
|
+
if (s === n.OpTimes && this.isMathMLApply(e, "times"))
|
|
302
|
+
e.appendChild(i);
|
|
303
|
+
else {
|
|
304
|
+
const t = this.doc.createElementNS(h, "apply"), r = this.doc.createElementNS(h, s === n.OpTimes ? "times" : "divide");
|
|
305
|
+
t.appendChild(r), t.appendChild(e), t.appendChild(i), e = t;
|
|
306
|
+
}
|
|
288
307
|
}
|
|
289
|
-
return
|
|
308
|
+
return e;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Checks if the identifier is a reserved MathML constant name
|
|
312
|
+
* and returns the corresponding element, or null if it's a variable.
|
|
313
|
+
*/
|
|
314
|
+
createMathMLConstant(e) {
|
|
315
|
+
const s = {
|
|
316
|
+
pi: "pi",
|
|
317
|
+
e: "exponentiale",
|
|
318
|
+
// Standard math constant e
|
|
319
|
+
inf: "infinity",
|
|
320
|
+
// Common abbreviation
|
|
321
|
+
infinity: "infinity",
|
|
322
|
+
NaN: "notanumber",
|
|
323
|
+
true: "true",
|
|
324
|
+
// Boolean constants
|
|
325
|
+
false: "false"
|
|
326
|
+
};
|
|
327
|
+
return s.hasOwnProperty(e) ? this.doc.createElementNS(h, s[e] || "") : null;
|
|
290
328
|
}
|
|
291
329
|
parseFactor() {
|
|
292
330
|
if (this.scanner.token === n.OpMinus) {
|
|
293
331
|
this.scanner.nextToken();
|
|
294
|
-
const
|
|
295
|
-
return s.appendChild(
|
|
332
|
+
const e = this.parseFactor(), s = this.doc.createElementNS(h, "apply"), i = this.doc.createElementNS(h, "minus");
|
|
333
|
+
return s.appendChild(i), s.appendChild(e), s;
|
|
296
334
|
}
|
|
297
335
|
if (this.scanner.token === n.Number) {
|
|
298
|
-
const
|
|
336
|
+
const e = this.scanner.value;
|
|
299
337
|
this.scanner.nextToken();
|
|
300
338
|
const s = this.doc.createElementNS(h, "cn");
|
|
301
|
-
if (s.textContent =
|
|
339
|
+
if (s.textContent = e, this.scanner.token === n.LBrace) {
|
|
302
340
|
if (this.scanner.nextToken(), this.scanner.value === "units") {
|
|
303
341
|
this.scanner.nextToken(), this.expect(n.Colon);
|
|
304
|
-
const
|
|
305
|
-
s.setAttributeNS(m, "cellml:units",
|
|
342
|
+
const i = this.expectValue(n.Identifier);
|
|
343
|
+
s.setAttributeNS(m, "cellml:units", i);
|
|
306
344
|
}
|
|
307
345
|
for (; this.scanner.token !== n.RBrace && this.scanner.token !== n.EOF; )
|
|
308
346
|
this.scanner.nextToken();
|
|
@@ -310,95 +348,98 @@ class g {
|
|
|
310
348
|
}
|
|
311
349
|
return s;
|
|
312
350
|
} else if (this.scanner.token === n.Identifier) {
|
|
313
|
-
const
|
|
351
|
+
const e = this.scanner.value;
|
|
314
352
|
if (this.scanner.nextToken(), this.scanner.token === n.LParam)
|
|
315
|
-
return this.parseFunctionCall(
|
|
316
|
-
const s = this.
|
|
317
|
-
|
|
353
|
+
return this.parseFunctionCall(e);
|
|
354
|
+
const s = this.createMathMLConstant(e);
|
|
355
|
+
if (s)
|
|
356
|
+
return s;
|
|
357
|
+
const i = this.doc.createElementNS(h, "ci");
|
|
358
|
+
return i.textContent = e, i;
|
|
318
359
|
} else if (this.scanner.token === n.LParam) {
|
|
319
360
|
this.scanner.nextToken();
|
|
320
|
-
const
|
|
321
|
-
return this.expect(n.RParam),
|
|
361
|
+
const e = this.parseExpression();
|
|
362
|
+
return this.expect(n.RParam), e;
|
|
322
363
|
} else if (this.scanner.token === n.KwSel)
|
|
323
364
|
return this.parsePiecewise();
|
|
324
365
|
throw new Error(`Unexpected token in math: ${this.scanner.value}`);
|
|
325
366
|
}
|
|
326
367
|
parsePiecewise() {
|
|
327
|
-
const
|
|
368
|
+
const e = this.doc.createElementNS(h, "piecewise");
|
|
328
369
|
for (this.expect(n.KwSel); this.scanner.token === n.KwCase; ) {
|
|
329
370
|
this.expect(n.KwCase);
|
|
330
371
|
const s = this.parseCondition();
|
|
331
372
|
this.expect(n.Colon);
|
|
332
|
-
const
|
|
373
|
+
const i = this.parseExpression();
|
|
333
374
|
this.expect(n.SemiColon);
|
|
334
|
-
const
|
|
335
|
-
|
|
375
|
+
const t = this.doc.createElementNS(h, "piece");
|
|
376
|
+
t.appendChild(i), t.appendChild(s), e.appendChild(t);
|
|
336
377
|
}
|
|
337
378
|
if (this.scanner.token === n.KwOtherwise) {
|
|
338
379
|
this.expect(n.KwOtherwise), this.expect(n.Colon);
|
|
339
380
|
const s = this.parseExpression();
|
|
340
381
|
this.expect(n.SemiColon);
|
|
341
|
-
const
|
|
342
|
-
|
|
382
|
+
const i = this.doc.createElementNS(h, "otherwise");
|
|
383
|
+
i.appendChild(s), e.appendChild(i);
|
|
343
384
|
}
|
|
344
|
-
return this.expect(n.KwEndSel),
|
|
385
|
+
return this.expect(n.KwEndSel), e;
|
|
345
386
|
}
|
|
346
|
-
parseFunctionCall(
|
|
347
|
-
if (this.expect(n.LParam),
|
|
348
|
-
const
|
|
387
|
+
parseFunctionCall(e) {
|
|
388
|
+
if (this.expect(n.LParam), e === "ode") {
|
|
389
|
+
const t = this.parseExpression();
|
|
349
390
|
this.expect(n.OpComma);
|
|
350
|
-
const
|
|
391
|
+
const r = this.parseExpression();
|
|
351
392
|
this.expect(n.RParam);
|
|
352
393
|
const c = this.doc.createElementNS(h, "apply");
|
|
353
394
|
c.appendChild(this.doc.createElementNS(h, "diff"));
|
|
354
395
|
const o = this.doc.createElementNS(h, "bvar");
|
|
355
|
-
return o.appendChild(
|
|
396
|
+
return o.appendChild(r), c.appendChild(o), c.appendChild(t), c;
|
|
356
397
|
}
|
|
357
|
-
const s = this.doc.createElementNS(h, "apply"),
|
|
358
|
-
if (s.appendChild(
|
|
398
|
+
const s = this.doc.createElementNS(h, "apply"), i = this.doc.createElementNS(h, e);
|
|
399
|
+
if (s.appendChild(i), this.scanner.token !== n.RParam)
|
|
359
400
|
do
|
|
360
401
|
this.scanner.token === n.OpComma && this.scanner.nextToken(), s.appendChild(this.parseExpression());
|
|
361
402
|
while (this.scanner.token === n.OpComma);
|
|
362
403
|
return this.expect(n.RParam), s;
|
|
363
404
|
}
|
|
364
405
|
// --- Helpers ---
|
|
365
|
-
expect(
|
|
366
|
-
if (this.scanner.token !==
|
|
367
|
-
throw new Error(`Syntax Error: Expected ${n[
|
|
406
|
+
expect(e) {
|
|
407
|
+
if (this.scanner.token !== e)
|
|
408
|
+
throw new Error(`Syntax Error: Expected ${n[e]} but found '${this.scanner.value}'`);
|
|
368
409
|
this.scanner.nextToken();
|
|
369
410
|
}
|
|
370
|
-
expectValue(
|
|
371
|
-
if (this.scanner.token !==
|
|
372
|
-
throw new Error(`Expected value of type ${n[
|
|
411
|
+
expectValue(e) {
|
|
412
|
+
if (this.scanner.token !== e)
|
|
413
|
+
throw new Error(`Expected value of type ${n[e]}, got ${this.scanner.token}`);
|
|
373
414
|
const s = this.scanner.value;
|
|
374
415
|
return this.scanner.nextToken(), s;
|
|
375
416
|
}
|
|
376
|
-
serialize(
|
|
377
|
-
const
|
|
417
|
+
serialize(e, s = 0) {
|
|
418
|
+
const i = " ".repeat(s), t = e.tagName, r = e.localName;
|
|
378
419
|
let c = "";
|
|
379
|
-
|
|
380
|
-
|
|
420
|
+
r === "model" && !e.hasAttribute("xmlns") && (c += ` xmlns="${m}"`), r === "math" && !e.hasAttribute("xmlns") && (c += ` xmlns="${h}" xmlns:cellml="${m}"`);
|
|
421
|
+
for (let d = 0; d < e.attributes.length; d++) {
|
|
422
|
+
const f = e.attributes[d];
|
|
381
423
|
if (f) {
|
|
382
424
|
if (this.sourceLineAttr && f.name === this.sourceLineAttr)
|
|
383
425
|
continue;
|
|
384
426
|
c += ` ${f.name}="${f.value}"`;
|
|
385
427
|
}
|
|
386
428
|
}
|
|
387
|
-
|
|
388
|
-
const o = Array.from(t.childNodes), u = o.some((d) => d.nodeType === 1), l = t.textContent?.trim();
|
|
429
|
+
const o = Array.from(e.childNodes), u = o.some((d) => d.nodeType === 1), l = e.textContent?.trim();
|
|
389
430
|
if (o.length === 0 && !l)
|
|
390
|
-
return `${
|
|
431
|
+
return `${i}<${t}${c}/>`;
|
|
391
432
|
if (!u)
|
|
392
|
-
return `${
|
|
393
|
-
let p = `${
|
|
433
|
+
return `${i}<${t}${c}>${l}</${t}>`;
|
|
434
|
+
let p = `${i}<${t}${c}>
|
|
394
435
|
`;
|
|
395
436
|
return o.forEach((d) => {
|
|
396
437
|
d.nodeType === 1 && (p += this.serialize(d, s + 1) + `
|
|
397
438
|
`);
|
|
398
|
-
}), p += `${
|
|
439
|
+
}), p += `${i}</${t}>`, p;
|
|
399
440
|
}
|
|
400
441
|
}
|
|
401
|
-
const
|
|
442
|
+
const $ = "http://www.cellml.org/cellml/2.0#";
|
|
402
443
|
class N {
|
|
403
444
|
output = "";
|
|
404
445
|
indentLevel = 0;
|
|
@@ -411,90 +452,90 @@ class N {
|
|
|
411
452
|
indent() {
|
|
412
453
|
return this.standardIndent.repeat(this.indentLevel);
|
|
413
454
|
}
|
|
414
|
-
append(
|
|
415
|
-
this.output += (s ? this.indent() : "") +
|
|
455
|
+
append(e, s = !0) {
|
|
456
|
+
this.output += (s ? this.indent() : "") + e + (s ? `
|
|
416
457
|
` : "");
|
|
417
458
|
}
|
|
418
459
|
// --- Main Entry Point ---
|
|
419
|
-
generate(
|
|
460
|
+
generate(e) {
|
|
420
461
|
this.output = "", this.indentLevel = 0;
|
|
421
462
|
try {
|
|
422
|
-
const s = this.domParser.parseFromString(
|
|
463
|
+
const s = this.domParser.parseFromString(e, "application/xml");
|
|
423
464
|
if (s.querySelector("parsererror")) throw new Error("XML Parsing Error");
|
|
424
|
-
const
|
|
425
|
-
if (!
|
|
426
|
-
this.processModel(
|
|
465
|
+
const t = s.getElementsByTagNameNS("http://www.cellml.org/cellml/2.0#", "model")[0];
|
|
466
|
+
if (!t) throw new Error("No CellML 2.0 Model found");
|
|
467
|
+
this.processModel(t);
|
|
427
468
|
} catch (s) {
|
|
428
469
|
return `// Error generating text: ${s.message}`;
|
|
429
470
|
}
|
|
430
471
|
return this.output;
|
|
431
472
|
}
|
|
432
473
|
// --- Recursive Processors ---
|
|
433
|
-
processModel(
|
|
434
|
-
const s =
|
|
474
|
+
processModel(e) {
|
|
475
|
+
const s = e.getAttribute("name") || "unnamed_model";
|
|
435
476
|
this.append(`def model ${s} as`), this.indentLevel++;
|
|
436
|
-
const
|
|
437
|
-
for (let
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
for (let
|
|
441
|
-
this.processComponent(
|
|
477
|
+
const i = e.getElementsByTagName("units");
|
|
478
|
+
for (let r = 0; r < i.length; r++)
|
|
479
|
+
i[r]?.parentElement === e && this.processUnits(i[r]);
|
|
480
|
+
const t = e.getElementsByTagName("component");
|
|
481
|
+
for (let r = 0; r < t.length; r++)
|
|
482
|
+
this.processComponent(t[r]);
|
|
442
483
|
this.indentLevel--, this.append("enddef;");
|
|
443
484
|
}
|
|
444
|
-
processUnits(
|
|
445
|
-
const s =
|
|
485
|
+
processUnits(e) {
|
|
486
|
+
const s = e?.getAttribute("name") || "unnamed_units";
|
|
446
487
|
this.append(`def unit ${s} as`), this.indentLevel++;
|
|
447
|
-
const
|
|
448
|
-
for (let
|
|
449
|
-
const
|
|
450
|
-
if (!
|
|
451
|
-
const c =
|
|
488
|
+
const i = e?.getElementsByTagName("unit") || [];
|
|
489
|
+
for (let t = 0; t < i.length; t++) {
|
|
490
|
+
const r = i[t];
|
|
491
|
+
if (!r) continue;
|
|
492
|
+
const c = r.getAttribute("prefix"), o = r.getAttribute("units"), u = r.getAttribute("exponent"), l = r.getAttribute("multiplier");
|
|
452
493
|
let p = `unit ${o}`;
|
|
453
494
|
c && (p += ` {prefix: ${c}}`), u && (p += ` {exponent: ${u}}`), l && (p += ` {multiplier: ${l}}`), p += ";", this.append(p);
|
|
454
495
|
}
|
|
455
496
|
this.indentLevel--, this.append("enddef;"), this.append("");
|
|
456
497
|
}
|
|
457
|
-
processComponent(
|
|
458
|
-
const s =
|
|
498
|
+
processComponent(e) {
|
|
499
|
+
const s = e?.getAttribute("name") || "unnamed_component";
|
|
459
500
|
this.append(`def comp ${s} as`), this.indentLevel++;
|
|
460
|
-
const
|
|
461
|
-
for (let
|
|
462
|
-
this.processVariable(
|
|
463
|
-
const
|
|
464
|
-
for (let
|
|
465
|
-
this.processMath(
|
|
501
|
+
const i = e?.getElementsByTagName("variable") || [];
|
|
502
|
+
for (let r = 0; r < i.length; r++)
|
|
503
|
+
this.processVariable(i[r]);
|
|
504
|
+
const t = e?.getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "math") || [];
|
|
505
|
+
for (let r = 0; r < t.length; r++)
|
|
506
|
+
this.processMath(t[r]);
|
|
466
507
|
this.indentLevel--, this.append("enddef;"), this.append("");
|
|
467
508
|
}
|
|
468
|
-
processVariable(
|
|
469
|
-
const s =
|
|
470
|
-
let c = `var ${s}: ${
|
|
471
|
-
|
|
509
|
+
processVariable(e) {
|
|
510
|
+
const s = e?.getAttribute("name"), i = e?.getAttribute("units"), t = e?.getAttribute("initial_value"), r = e?.getAttribute("interface");
|
|
511
|
+
let c = `var ${s}: ${i}`, o = [];
|
|
512
|
+
t && o.push(`init: ${t}`), r && o.push(`interface: ${r}`), o.length > 0 && (c += ` {${o.join(", ")}}`), c += ";", this.append(c);
|
|
472
513
|
}
|
|
473
514
|
// --- MathML Handling ---
|
|
474
|
-
processMath(
|
|
475
|
-
const s = Array.from(
|
|
476
|
-
for (const
|
|
477
|
-
if (
|
|
478
|
-
const
|
|
515
|
+
processMath(e) {
|
|
516
|
+
const s = Array.from(e?.children || []);
|
|
517
|
+
for (const i of s)
|
|
518
|
+
if (i.localName === "apply" && i.firstElementChild?.localName === "eq") {
|
|
519
|
+
const r = Array.from(i.children).slice(1), c = this.parseMathNode(r[0]), o = this.parseMathNode(r[1]);
|
|
479
520
|
this.append(`${c} = ${o};`);
|
|
480
521
|
} else {
|
|
481
|
-
const
|
|
482
|
-
|
|
522
|
+
const r = this.parseMathNode(i);
|
|
523
|
+
r && this.append(r + ";");
|
|
483
524
|
}
|
|
484
525
|
}
|
|
485
|
-
parseMathNode(
|
|
486
|
-
if (!
|
|
487
|
-
const s =
|
|
526
|
+
parseMathNode(e) {
|
|
527
|
+
if (!e) return "";
|
|
528
|
+
const s = e.localName;
|
|
488
529
|
if (s === "apply")
|
|
489
|
-
return this.parseApply(
|
|
530
|
+
return this.parseApply(e);
|
|
490
531
|
if (s === "ci")
|
|
491
|
-
return
|
|
532
|
+
return e.textContent?.trim() || "";
|
|
492
533
|
if (s === "cn") {
|
|
493
|
-
const
|
|
494
|
-
return
|
|
534
|
+
const i = e.textContent?.trim() || "0", t = e.getAttributeNS($, "units");
|
|
535
|
+
return t ? `${i} {units: ${t}}` : i;
|
|
495
536
|
} else {
|
|
496
537
|
if (s === "piecewise")
|
|
497
|
-
return this.parsePiecewise(
|
|
538
|
+
return this.parsePiecewise(e);
|
|
498
539
|
if (s === "pi")
|
|
499
540
|
return "pi";
|
|
500
541
|
if (s === "bvar")
|
|
@@ -502,37 +543,37 @@ class N {
|
|
|
502
543
|
}
|
|
503
544
|
return console.log(`Unsupported MathML node: ${s}`), `/* Unsupported MathML node: ${s} */`;
|
|
504
545
|
}
|
|
505
|
-
parseApply(
|
|
506
|
-
const s = Array.from(
|
|
546
|
+
parseApply(e) {
|
|
547
|
+
const s = Array.from(e.children);
|
|
507
548
|
if (s.length === 0) return "";
|
|
508
|
-
const
|
|
509
|
-
switch (
|
|
549
|
+
const i = s[0]?.localName, t = s.slice(1).map((r) => this.parseMathNode(r));
|
|
550
|
+
switch (i) {
|
|
510
551
|
case "plus":
|
|
511
|
-
return `(${
|
|
552
|
+
return `(${t.join(" + ")})`;
|
|
512
553
|
case "minus":
|
|
513
|
-
return
|
|
554
|
+
return t.length === 1 ? `-${t[0]}` : `(${t[0]} - ${t[1]})`;
|
|
514
555
|
case "times":
|
|
515
|
-
return `(${
|
|
556
|
+
return `(${t.join(" * ")})`;
|
|
516
557
|
case "divide":
|
|
517
|
-
return `(${
|
|
558
|
+
return `(${t[0]} / ${t[1]})`;
|
|
518
559
|
case "eq":
|
|
519
|
-
return `${
|
|
560
|
+
return `${t[0]} == ${t[1]}`;
|
|
520
561
|
case "neq":
|
|
521
|
-
return `${
|
|
562
|
+
return `${t[0]} != ${t[1]}`;
|
|
522
563
|
case "lt":
|
|
523
|
-
return `${
|
|
564
|
+
return `${t[0]} < ${t[1]}`;
|
|
524
565
|
case "leq":
|
|
525
|
-
return `${
|
|
566
|
+
return `${t[0]} <= ${t[1]}`;
|
|
526
567
|
case "gt":
|
|
527
|
-
return `${
|
|
568
|
+
return `${t[0]} > ${t[1]}`;
|
|
528
569
|
case "geq":
|
|
529
|
-
return `${
|
|
570
|
+
return `${t[0]} >= ${t[1]}`;
|
|
530
571
|
case "and":
|
|
531
|
-
return `${
|
|
572
|
+
return `${t.join(" and ")}`;
|
|
532
573
|
case "or":
|
|
533
|
-
return `${
|
|
574
|
+
return `${t.join(" or ")}`;
|
|
534
575
|
case "diff":
|
|
535
|
-
const
|
|
576
|
+
const r = s.find((l) => l.localName === "bvar"), c = s.find((l) => l.localName !== "diff" && l.localName !== "bvar"), o = r?.children[0]?.textContent || "t";
|
|
536
577
|
return `ode(${c ? this.parseMathNode(c) : "unknown"}, ${o})`;
|
|
537
578
|
// Functions
|
|
538
579
|
case "sin":
|
|
@@ -541,22 +582,22 @@ class N {
|
|
|
541
582
|
case "exp":
|
|
542
583
|
case "ln":
|
|
543
584
|
case "log":
|
|
544
|
-
return `${
|
|
585
|
+
return `${i}(${t[0]})`;
|
|
545
586
|
case "root":
|
|
546
|
-
return `sqrt(${
|
|
587
|
+
return `sqrt(${t[0]})`;
|
|
547
588
|
default:
|
|
548
|
-
return `${
|
|
589
|
+
return `${i}(${t.join(", ")})`;
|
|
549
590
|
}
|
|
550
591
|
}
|
|
551
|
-
parsePiecewise(
|
|
592
|
+
parsePiecewise(e) {
|
|
552
593
|
let s = [];
|
|
553
|
-
return Array.from(
|
|
554
|
-
if (
|
|
555
|
-
const
|
|
556
|
-
s.push(`${this.standardIndent}case ${c}: ${
|
|
557
|
-
} else if (
|
|
558
|
-
const
|
|
559
|
-
s.push(`${this.standardIndent}otherwise: ${
|
|
594
|
+
return Array.from(e?.children || []).forEach((t) => {
|
|
595
|
+
if (t.localName === "piece") {
|
|
596
|
+
const r = this.parseMathNode(t.children[0]), c = this.parseMathNode(t.children[1]);
|
|
597
|
+
s.push(`${this.standardIndent}case ${c}: ${r};`);
|
|
598
|
+
} else if (t.localName === "otherwise") {
|
|
599
|
+
const r = this.parseMathNode(t.children[0]);
|
|
600
|
+
s.push(`${this.standardIndent}otherwise: ${r};`);
|
|
560
601
|
}
|
|
561
602
|
}), `sel
|
|
562
603
|
${this.indent()}${s.join(`
|
|
@@ -565,23 +606,23 @@ ${this.indent()}endsel`;
|
|
|
565
606
|
}
|
|
566
607
|
}
|
|
567
608
|
class C {
|
|
568
|
-
convert(
|
|
569
|
-
if (!
|
|
570
|
-
if (
|
|
571
|
-
return Array.from(
|
|
609
|
+
convert(e) {
|
|
610
|
+
if (!e) return "";
|
|
611
|
+
if (e.localName === "math")
|
|
612
|
+
return Array.from(e.children).map((s) => this.convert(s)).join(`
|
|
572
613
|
`);
|
|
573
|
-
if (
|
|
574
|
-
const s = Array.from(
|
|
575
|
-
return `${
|
|
614
|
+
if (e.localName === "apply" && e.firstElementChild?.localName === "eq") {
|
|
615
|
+
const s = Array.from(e.children), i = this.parseNode(s[1]), t = this.parseNode(s[2]);
|
|
616
|
+
return `${i} = ${t}`;
|
|
576
617
|
}
|
|
577
|
-
return this.parseNode(
|
|
618
|
+
return this.parseNode(e);
|
|
578
619
|
}
|
|
579
|
-
parseNode(
|
|
580
|
-
if (!
|
|
581
|
-
const s =
|
|
582
|
-
return s === "apply" ? this.parseApply(
|
|
620
|
+
parseNode(e) {
|
|
621
|
+
if (!e) return "";
|
|
622
|
+
const s = e.localName;
|
|
623
|
+
return s === "apply" ? this.parseApply(e) : s === "ci" ? this.parseIdentifier(e.textContent || "") : s === "cn" ? e.textContent || "0" : s === "piecewise" ? this.parsePiecewise(e) : "";
|
|
583
624
|
}
|
|
584
|
-
escapeGreek(
|
|
625
|
+
escapeGreek(e) {
|
|
585
626
|
return [
|
|
586
627
|
"alpha",
|
|
587
628
|
"beta",
|
|
@@ -607,72 +648,72 @@ class C {
|
|
|
607
648
|
"chi",
|
|
608
649
|
"psi",
|
|
609
650
|
"omega"
|
|
610
|
-
].includes(
|
|
651
|
+
].includes(e.toLowerCase()) ? `\\${e}` : e;
|
|
611
652
|
}
|
|
612
653
|
/**
|
|
613
654
|
* Specialized identifier formatter.
|
|
614
655
|
* Format: Base_Sub_Super_SubOfSuper
|
|
615
656
|
* Example: v_AQ_api_i -> v_{AQ}^{api_{i}}
|
|
616
657
|
*/
|
|
617
|
-
parseIdentifier(
|
|
618
|
-
if (!
|
|
619
|
-
return this.escapeGreek(
|
|
620
|
-
const s =
|
|
621
|
-
s[1] &&
|
|
622
|
-
let
|
|
623
|
-
s.length === 3 && (s[2] || []).length === 1 ?
|
|
624
|
-
|
|
658
|
+
parseIdentifier(e) {
|
|
659
|
+
if (!e.includes("_"))
|
|
660
|
+
return this.escapeGreek(e);
|
|
661
|
+
const s = e.split("_"), i = this.escapeGreek(s[0] || ""), t = [];
|
|
662
|
+
s[1] && t.push(s[1]), s.length > 4 && t.push(...s.slice(4));
|
|
663
|
+
let r = "";
|
|
664
|
+
s.length === 3 && (s[2] || []).length === 1 ? t.push(this.escapeGreek(s[2] || "")) : s[2] && (r = this.escapeGreek(s[2]), s[3] && (r += `_{${this.escapeGreek(s[3])}}`)), t.forEach((u, l) => {
|
|
665
|
+
t[l] = this.escapeGreek(u);
|
|
625
666
|
});
|
|
626
|
-
const c =
|
|
627
|
-
let o =
|
|
628
|
-
return c && (o += `_{${c}}`),
|
|
667
|
+
const c = t.join(",");
|
|
668
|
+
let o = i;
|
|
669
|
+
return c && (o += `_{${c}}`), r && (o += `^{${r}}`), o;
|
|
629
670
|
}
|
|
630
|
-
parseApply(
|
|
631
|
-
const s = Array.from(
|
|
632
|
-
switch (
|
|
671
|
+
parseApply(e) {
|
|
672
|
+
const s = Array.from(e?.children || []), i = s[0]?.localName, t = s.slice(1).map((r) => this.parseNode(r));
|
|
673
|
+
switch (i) {
|
|
633
674
|
case "plus":
|
|
634
|
-
return
|
|
675
|
+
return t.join(" + ");
|
|
635
676
|
case "minus":
|
|
636
|
-
return
|
|
677
|
+
return t.length === 1 ? `-${t[0]}` : `${t[0]} - ${t[1]}`;
|
|
637
678
|
case "times":
|
|
638
|
-
return
|
|
679
|
+
return t.join(" \\cdot ");
|
|
639
680
|
case "divide":
|
|
640
|
-
return `\\frac{${
|
|
681
|
+
return `\\frac{${t[0]}}{${t[1]}}`;
|
|
641
682
|
case "eq":
|
|
642
|
-
return `${
|
|
683
|
+
return `${t[0]} == ${t[1]}`;
|
|
643
684
|
case "neq":
|
|
644
|
-
return `${
|
|
685
|
+
return `${t[0]} \\neq ${t[1]}`;
|
|
645
686
|
case "lt":
|
|
646
|
-
return `${
|
|
687
|
+
return `${t[0]} < ${t[1]}`;
|
|
647
688
|
case "leq":
|
|
648
|
-
return `${
|
|
689
|
+
return `${t[0]} \\leq ${t[1]}`;
|
|
649
690
|
case "gt":
|
|
650
|
-
return `${
|
|
691
|
+
return `${t[0]} > ${t[1]}`;
|
|
651
692
|
case "geq":
|
|
652
|
-
return `${
|
|
693
|
+
return `${t[0]} \\geq ${t[1]}`;
|
|
653
694
|
case "and":
|
|
654
|
-
return
|
|
695
|
+
return t.join(" \\land ");
|
|
655
696
|
case "or":
|
|
656
|
-
return
|
|
697
|
+
return t.join(" \\lor ");
|
|
657
698
|
case "power":
|
|
658
|
-
const
|
|
659
|
-
return
|
|
699
|
+
const r = s[1], c = t[0] || "", o = t[1];
|
|
700
|
+
return r?.localName === "ci" || r?.localName === "cn" && !c.trim().startsWith("-") ? `{${c}}^{${o}}` : `\\left({${c}}\\right)^{${o}}`;
|
|
660
701
|
case "root":
|
|
661
702
|
case "sqrt":
|
|
662
|
-
return `\\sqrt{${
|
|
703
|
+
return `\\sqrt{${t[0]}}`;
|
|
663
704
|
// simple sqrt
|
|
664
705
|
case "diff":
|
|
665
706
|
const l = s.find((w) => w.localName === "bvar"), p = s.find((w) => w.localName !== "diff" && w.localName !== "bvar"), d = l ? this.parseNode(l.firstElementChild) : "x";
|
|
666
707
|
return `\\frac{d${p ? this.parseNode(p) : "y"}}{d${d}}`;
|
|
667
708
|
// Trig & Funcs
|
|
668
709
|
case "exp":
|
|
669
|
-
return `e^{${
|
|
710
|
+
return `e^{${t[0]}}`;
|
|
670
711
|
case "abs":
|
|
671
|
-
return `\\left|${
|
|
712
|
+
return `\\left|${t[0]}\\right|`;
|
|
672
713
|
case "floor":
|
|
673
|
-
return `\\lfloor ${
|
|
714
|
+
return `\\lfloor ${t[0]} \\rfloor`;
|
|
674
715
|
case "ceil":
|
|
675
|
-
return `\\lceil ${
|
|
716
|
+
return `\\lceil ${t[0]} \\rceil`;
|
|
676
717
|
case "cos":
|
|
677
718
|
case "cosh":
|
|
678
719
|
case "log10":
|
|
@@ -684,20 +725,20 @@ class C {
|
|
|
684
725
|
case "sinh":
|
|
685
726
|
case "tan":
|
|
686
727
|
case "tanh":
|
|
687
|
-
return `\\${
|
|
728
|
+
return `\\${i}\\left(${t[0]}\\right)`;
|
|
688
729
|
default:
|
|
689
|
-
return console.log(`Unsupported MathML operator: ${
|
|
730
|
+
return console.log(`Unsupported MathML operator: ${i}`), `\\text{${i}}(${t.join(", ")})`;
|
|
690
731
|
}
|
|
691
732
|
}
|
|
692
|
-
parsePiecewise(
|
|
733
|
+
parsePiecewise(e) {
|
|
693
734
|
let s = "";
|
|
694
|
-
return Array.from(
|
|
695
|
-
if (
|
|
696
|
-
const
|
|
697
|
-
s += `${
|
|
698
|
-
} else if (
|
|
699
|
-
const
|
|
700
|
-
s += `${
|
|
735
|
+
return Array.from(e.children).forEach((t) => {
|
|
736
|
+
if (t.localName === "piece") {
|
|
737
|
+
const r = this.parseNode(t.children[0]), c = this.parseNode(t.children[1]);
|
|
738
|
+
s += `${r} & \\text{if } ${c} \\\\ `;
|
|
739
|
+
} else if (t.localName === "otherwise") {
|
|
740
|
+
const r = this.parseNode(t.children[0]);
|
|
741
|
+
s += `${r} & \\text{otherwise}`;
|
|
701
742
|
}
|
|
702
743
|
}), `\\begin{cases} ${s} \\end{cases}`;
|
|
703
744
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
(function(m,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(m=typeof globalThis<"u"?globalThis:m||self,n(m.CellMLTextEditor={}))})(this,(function(m){"use strict";var n=(
|
|
2
|
-
`&&
|
|
3
|
-
`;)this.pos++;else break}return
|
|
4
|
-
`+this.serialize(s),errors:[]}}catch(s){return{xml:null,errors:[{line:this.scanner.getLine(),message:s.message||"Unknown parsing error"}]}}}parseBlock(
|
|
5
|
-
`;return
|
|
6
|
-
`)}),p+=`${
|
|
7
|
-
`:"")}generate(
|
|
1
|
+
(function(m,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(m=typeof globalThis<"u"?globalThis:m||self,n(m.CellMLTextEditor={}))})(this,(function(m){"use strict";var n=(a=>(a[a.Unknown=0]="Unknown",a[a.EOF=1]="EOF",a[a.Identifier=2]="Identifier",a[a.Number=3]="Number",a[a.String=4]="String",a[a.KwDef=5]="KwDef",a[a.KwModel=6]="KwModel",a[a.KwComp=7]="KwComp",a[a.KwEndDef=8]="KwEndDef",a[a.KwAs=9]="KwAs",a[a.KwVar=10]="KwVar",a[a.KwUnit=11]="KwUnit",a[a.KwSel=12]="KwSel",a[a.KwCase=13]="KwCase",a[a.KwOtherwise=14]="KwOtherwise",a[a.KwEndSel=15]="KwEndSel",a[a.OpAss=16]="OpAss",a[a.OpPlus=17]="OpPlus",a[a.OpMinus=18]="OpMinus",a[a.OpTimes=19]="OpTimes",a[a.OpDivide=20]="OpDivide",a[a.OpComma=21]="OpComma",a[a.Colon=22]="Colon",a[a.SemiColon=23]="SemiColon",a[a.LParam=24]="LParam",a[a.RParam=25]="RParam",a[a.LBrace=26]="LBrace",a[a.RBrace=27]="RBrace",a[a.OpEq=28]="OpEq",a[a.OpNe=29]="OpNe",a[a.OpLt=30]="OpLt",a[a.OpLe=31]="OpLe",a[a.OpGt=32]="OpGt",a[a.OpGe=33]="OpGe",a[a.OpAnd=34]="OpAnd",a[a.OpOr=35]="OpOr",a))(n||{});class ${input;pos=0;line=1;length;currentToken=1;currentValue="";constructor(e){this.input=e,this.length=e.length,this.nextToken()}get token(){return this.currentToken}get value(){return this.currentValue}nextToken(){const e=this.skipWhitespace();if(this.line+=e,this.pos>=this.length){this.currentToken=1;return}const s=this.input[this.pos];if(/[a-zA-Z_]/.test(s||"")){let i=this.pos;for(;this.pos<this.length&&/[a-zA-Z0-9_]/.test(this.input[this.pos]||"");)this.pos++;const t=this.input.slice(i,this.pos);this.currentValue=t,this.currentToken=this.getKeywordType(t);return}if(/[0-9]/.test(s||"")||s==="."&&/[0-9]/.test(this.input[this.pos+1]||"")){let i=this.pos;for(;this.pos<this.length&&/[0-9]/.test(this.input[this.pos]||"");)this.pos++;if(this.input[this.pos]===".")for(this.pos++;this.pos<this.length&&/[0-9]/.test(this.input[this.pos]||"");)this.pos++;if(this.input[this.pos]==="e"||this.input[this.pos]==="E")for(this.pos++,(this.input[this.pos]==="+"||this.input[this.pos]==="-")&&this.pos++;this.pos<this.length&&/[0-9]/.test(this.input[this.pos]||"");)this.pos++;this.currentValue=this.input.slice(i,this.pos),this.currentToken=3;return}switch(this.pos++,this.currentValue=s||"",s){case"=":this.input[this.pos]==="="?(this.pos++,this.currentValue="==",this.currentToken=28):this.currentToken=16;break;case"!":this.input[this.pos]==="="?(this.pos++,this.currentValue="!=",this.currentToken=29):(console.warn(`Unexpected character '!' at pos ${this.pos}`),this.currentToken=0);break;case"<":this.input[this.pos]==="="?(this.pos++,this.currentValue="<=",this.currentToken=31):this.currentToken=30;break;case">":this.input[this.pos]==="="?(this.pos++,this.currentValue=">=",this.currentToken=33):this.currentToken=32;break;case"+":this.currentToken=17;break;case"-":this.currentToken=18;break;case"*":this.currentToken=19;break;case"/":this.currentToken=20;break;case"(":this.currentToken=24;break;case")":this.currentToken=25;break;case"{":this.currentToken=26;break;case"}":this.currentToken=27;break;case":":this.currentToken=22;break;case";":this.currentToken=23;break;case",":this.currentToken=21;break;default:console.warn("Unknown char:",s),this.currentToken=0}}getLine(){return this.line}skipWhitespace(){let e=0;for(;this.pos<this.length;){const s=this.input[this.pos];if(/\s/.test(s||""))s===`
|
|
2
|
+
`&&e++,this.pos++;else if(s==="/"&&this.input[this.pos+1]==="/")for(this.pos+=2;this.pos<this.length&&this.input[this.pos]!==`
|
|
3
|
+
`;)this.pos++;else break}return e}getKeywordType(e){switch(e){case"def":return 5;case"model":return 6;case"comp":return 7;case"enddef":return 8;case"as":return 9;case"var":return 10;case"unit":return 11;case"sel":return 12;case"case":return 13;case"otherwise":return 14;case"endsel":return 15;case"and":return 34;case"or":return 35;default:return 2}}}const f="http://www.cellml.org/cellml/2.0#",o="http://www.w3.org/1998/Math/MathML";class g{scanner;doc;sourceLineAttr;constructor(e={}){this.sourceLineAttr=e.sourceLineAttribute===void 0?"data-source-location":e.sourceLineAttribute}parse(e){this.scanner=new $(e),this.doc=document.implementation.createDocument(f,"model",null);try{const s=this.doc.documentElement;for(this.expect(n.KwDef),this.expect(n.KwModel),this.scanner.token===n.Identifier&&(s.setAttribute("name",this.scanner.value),this.scanner.nextToken()),this.expect(n.KwAs);this.scanner.token!==n.KwEndDef&&this.scanner.token!==n.EOF;)this.scanner.token===n.KwDef?this.parseBlock(s):this.scanner.nextToken();return this.expect(n.KwEndDef),this.expect(n.SemiColon),{xml:`<?xml version="1.0" encoding="UTF-8"?>
|
|
4
|
+
`+this.serialize(s),errors:[]}}catch(s){return{xml:null,errors:[{line:this.scanner.getLine(),message:s.message||"Unknown parsing error"}]}}}parseBlock(e){if(this.expect(n.KwDef),this.scanner.token===n.KwComp)this.parseComponent(e);else if(this.scanner.token===n.KwUnit)this.parseUnit(e);else throw new Error("Expected 'comp' or 'unit' after 'def'")}parseComponent(e){this.expect(n.KwComp);const s=this.expectValue(n.Identifier);this.expect(n.KwAs);const i=this.doc.createElementNS(f,"component");for(i.setAttribute("name",s),e.appendChild(i);this.scanner.token!==n.KwEndDef&&this.scanner.token!==n.EOF;)this.scanner.token===n.KwVar?this.parseVariable(i):this.scanner.token===n.Identifier||this.scanner.token===n.KwSel?this.parseMathEquation(i):this.scanner.nextToken();this.expect(n.KwEndDef),this.expect(n.SemiColon)}parseVariable(e){this.expect(n.KwVar);const s=this.expectValue(n.Identifier);this.expect(n.Colon);const i=this.expectValue(n.Identifier),t=this.doc.createElementNS(f,"variable");if(t.setAttribute("name",s),t.setAttribute("units",i),this.scanner.token===n.LBrace){for(this.scanner.nextToken();this.scanner.token!==n.RBrace&&this.scanner.token!==n.EOF;){const r=this.expectValue(n.Identifier);this.expect(n.Colon);let c="";this.scanner.token===n.OpMinus?(this.scanner.nextToken(),c="-"+this.expectValue(n.Number)):this.scanner.token===n.Number?c=this.expectValue(n.Number):c=this.expectValue(n.Identifier),r==="init"?t.setAttribute("initial_value",c):r==="interface"&&t.setAttribute("interface",c),this.scanner.token===n.OpComma&&this.scanner.nextToken()}this.expect(n.RBrace)}this.expect(n.SemiColon),e.appendChild(t)}parseUnit(e){for(this.expect(n.KwUnit);this.scanner.token!==n.KwEndDef&&this.scanner.token!==n.EOF;)this.scanner.nextToken();this.expect(n.KwEndDef),this.expect(n.SemiColon)}parseMathEquation(e){const s=this.scanner.getLine();let i=e.getElementsByTagNameNS(o,"math")[0];i||(i=this.doc.createElementNS(o,"math"),e.appendChild(i));const t=this.doc.createElementNS(o,"apply"),r=this.doc.createElementNS(o,"eq");t.appendChild(r);const c=this.parseExpression();this.expect(n.OpAss);const h=this.parseExpression(),u=this.scanner.getLine();this.sourceLineAttr&&t.setAttribute(this.sourceLineAttr,`${s.toString()}`+(u!==s?`-${u.toString()}`:"")),t.appendChild(c),t.appendChild(h),i.appendChild(t),this.expect(n.SemiColon)}parseCondition(){let e=this.parseComparison();for(;this.scanner.token===n.OpAnd||this.scanner.token===n.OpOr;){const s=this.scanner.token;this.scanner.nextToken();const i=this.parseComparison(),t=this.doc.createElementNS(o,"apply"),r=this.doc.createElementNS(o,s===n.OpAnd?"and":"or");t.appendChild(r),t.appendChild(e),t.appendChild(i),e=t}return e}isComparisonToken(e){return[n.OpEq,n.OpNe,n.OpLt,n.OpLe,n.OpGt,n.OpGe].includes(e)}parseComparison(){let e=this.parseExpression();if(this.isComparisonToken(this.scanner.token)){const s=this.scanner.token;this.scanner.nextToken();const i=this.parseExpression(),t=this.doc.createElementNS(o,"apply");let r="";switch(s){case n.OpEq:r="eq";break;case n.OpNe:r="neq";break;case n.OpLt:r="lt";break;case n.OpLe:r="leq";break;case n.OpGt:r="gt";break;case n.OpGe:r="geq";break;case n.OpAnd:r="and";break}const c=this.doc.createElementNS(o,r);return t.appendChild(c),t.appendChild(e),t.appendChild(i),t}return e}isMathMLApply(e,s){if(e.localName!=="apply")return!1;const i=e.firstElementChild;return i?i.localName===s:!1}parseExpression(){let e=this.parseTerm();for(;this.scanner.token===n.OpPlus||this.scanner.token===n.OpMinus;){const s=this.scanner.token;this.scanner.nextToken();const i=this.parseTerm();if(s===n.OpPlus&&this.isMathMLApply(e,"plus"))e.appendChild(i);else{const t=this.doc.createElementNS(o,"apply"),r=this.doc.createElementNS(o,s===n.OpPlus?"plus":"minus");t.appendChild(r),t.appendChild(e),t.appendChild(i),e=t}}return e}parseTerm(){let e=this.parseFactor();for(;this.scanner.token===n.OpTimes||this.scanner.token===n.OpDivide;){const s=this.scanner.token;this.scanner.nextToken();const i=this.parseFactor();if(s===n.OpTimes&&this.isMathMLApply(e,"times"))e.appendChild(i);else{const t=this.doc.createElementNS(o,"apply"),r=this.doc.createElementNS(o,s===n.OpTimes?"times":"divide");t.appendChild(r),t.appendChild(e),t.appendChild(i),e=t}}return e}createMathMLConstant(e){const s={pi:"pi",e:"exponentiale",inf:"infinity",infinity:"infinity",NaN:"notanumber",true:"true",false:"false"};return s.hasOwnProperty(e)?this.doc.createElementNS(o,s[e]||""):null}parseFactor(){if(this.scanner.token===n.OpMinus){this.scanner.nextToken();const e=this.parseFactor(),s=this.doc.createElementNS(o,"apply"),i=this.doc.createElementNS(o,"minus");return s.appendChild(i),s.appendChild(e),s}if(this.scanner.token===n.Number){const e=this.scanner.value;this.scanner.nextToken();const s=this.doc.createElementNS(o,"cn");if(s.textContent=e,this.scanner.token===n.LBrace){if(this.scanner.nextToken(),this.scanner.value==="units"){this.scanner.nextToken(),this.expect(n.Colon);const i=this.expectValue(n.Identifier);s.setAttributeNS(f,"cellml:units",i)}for(;this.scanner.token!==n.RBrace&&this.scanner.token!==n.EOF;)this.scanner.nextToken();this.expect(n.RBrace)}return s}else if(this.scanner.token===n.Identifier){const e=this.scanner.value;if(this.scanner.nextToken(),this.scanner.token===n.LParam)return this.parseFunctionCall(e);const s=this.createMathMLConstant(e);if(s)return s;const i=this.doc.createElementNS(o,"ci");return i.textContent=e,i}else if(this.scanner.token===n.LParam){this.scanner.nextToken();const e=this.parseExpression();return this.expect(n.RParam),e}else if(this.scanner.token===n.KwSel)return this.parsePiecewise();throw new Error(`Unexpected token in math: ${this.scanner.value}`)}parsePiecewise(){const e=this.doc.createElementNS(o,"piecewise");for(this.expect(n.KwSel);this.scanner.token===n.KwCase;){this.expect(n.KwCase);const s=this.parseCondition();this.expect(n.Colon);const i=this.parseExpression();this.expect(n.SemiColon);const t=this.doc.createElementNS(o,"piece");t.appendChild(i),t.appendChild(s),e.appendChild(t)}if(this.scanner.token===n.KwOtherwise){this.expect(n.KwOtherwise),this.expect(n.Colon);const s=this.parseExpression();this.expect(n.SemiColon);const i=this.doc.createElementNS(o,"otherwise");i.appendChild(s),e.appendChild(i)}return this.expect(n.KwEndSel),e}parseFunctionCall(e){if(this.expect(n.LParam),e==="ode"){const t=this.parseExpression();this.expect(n.OpComma);const r=this.parseExpression();this.expect(n.RParam);const c=this.doc.createElementNS(o,"apply");c.appendChild(this.doc.createElementNS(o,"diff"));const h=this.doc.createElementNS(o,"bvar");return h.appendChild(r),c.appendChild(h),c.appendChild(t),c}const s=this.doc.createElementNS(o,"apply"),i=this.doc.createElementNS(o,e);if(s.appendChild(i),this.scanner.token!==n.RParam)do this.scanner.token===n.OpComma&&this.scanner.nextToken(),s.appendChild(this.parseExpression());while(this.scanner.token===n.OpComma);return this.expect(n.RParam),s}expect(e){if(this.scanner.token!==e)throw new Error(`Syntax Error: Expected ${n[e]} but found '${this.scanner.value}'`);this.scanner.nextToken()}expectValue(e){if(this.scanner.token!==e)throw new Error(`Expected value of type ${n[e]}, got ${this.scanner.token}`);const s=this.scanner.value;return this.scanner.nextToken(),s}serialize(e,s=0){const i=" ".repeat(s),t=e.tagName,r=e.localName;let c="";r==="model"&&!e.hasAttribute("xmlns")&&(c+=` xmlns="${f}"`),r==="math"&&!e.hasAttribute("xmlns")&&(c+=` xmlns="${o}" xmlns:cellml="${f}"`);for(let d=0;d<e.attributes.length;d++){const w=e.attributes[d];if(w){if(this.sourceLineAttr&&w.name===this.sourceLineAttr)continue;c+=` ${w.name}="${w.value}"`}}const h=Array.from(e.childNodes),u=h.some(d=>d.nodeType===1),l=e.textContent?.trim();if(h.length===0&&!l)return`${i}<${t}${c}/>`;if(!u)return`${i}<${t}${c}>${l}</${t}>`;let p=`${i}<${t}${c}>
|
|
5
|
+
`;return h.forEach(d=>{d.nodeType===1&&(p+=this.serialize(d,s+1)+`
|
|
6
|
+
`)}),p+=`${i}</${t}>`,p}}const C="http://www.cellml.org/cellml/2.0#";class N{output="";indentLevel=0;domParser;standardIndent=" ";constructor(){this.domParser=new DOMParser}indent(){return this.standardIndent.repeat(this.indentLevel)}append(e,s=!0){this.output+=(s?this.indent():"")+e+(s?`
|
|
7
|
+
`:"")}generate(e){this.output="",this.indentLevel=0;try{const s=this.domParser.parseFromString(e,"application/xml");if(s.querySelector("parsererror"))throw new Error("XML Parsing Error");const t=s.getElementsByTagNameNS("http://www.cellml.org/cellml/2.0#","model")[0];if(!t)throw new Error("No CellML 2.0 Model found");this.processModel(t)}catch(s){return`// Error generating text: ${s.message}`}return this.output}processModel(e){const s=e.getAttribute("name")||"unnamed_model";this.append(`def model ${s} as`),this.indentLevel++;const i=e.getElementsByTagName("units");for(let r=0;r<i.length;r++)i[r]?.parentElement===e&&this.processUnits(i[r]);const t=e.getElementsByTagName("component");for(let r=0;r<t.length;r++)this.processComponent(t[r]);this.indentLevel--,this.append("enddef;")}processUnits(e){const s=e?.getAttribute("name")||"unnamed_units";this.append(`def unit ${s} as`),this.indentLevel++;const i=e?.getElementsByTagName("unit")||[];for(let t=0;t<i.length;t++){const r=i[t];if(!r)continue;const c=r.getAttribute("prefix"),h=r.getAttribute("units"),u=r.getAttribute("exponent"),l=r.getAttribute("multiplier");let p=`unit ${h}`;c&&(p+=` {prefix: ${c}}`),u&&(p+=` {exponent: ${u}}`),l&&(p+=` {multiplier: ${l}}`),p+=";",this.append(p)}this.indentLevel--,this.append("enddef;"),this.append("")}processComponent(e){const s=e?.getAttribute("name")||"unnamed_component";this.append(`def comp ${s} as`),this.indentLevel++;const i=e?.getElementsByTagName("variable")||[];for(let r=0;r<i.length;r++)this.processVariable(i[r]);const t=e?.getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML","math")||[];for(let r=0;r<t.length;r++)this.processMath(t[r]);this.indentLevel--,this.append("enddef;"),this.append("")}processVariable(e){const s=e?.getAttribute("name"),i=e?.getAttribute("units"),t=e?.getAttribute("initial_value"),r=e?.getAttribute("interface");let c=`var ${s}: ${i}`,h=[];t&&h.push(`init: ${t}`),r&&h.push(`interface: ${r}`),h.length>0&&(c+=` {${h.join(", ")}}`),c+=";",this.append(c)}processMath(e){const s=Array.from(e?.children||[]);for(const i of s)if(i.localName==="apply"&&i.firstElementChild?.localName==="eq"){const r=Array.from(i.children).slice(1),c=this.parseMathNode(r[0]),h=this.parseMathNode(r[1]);this.append(`${c} = ${h};`)}else{const r=this.parseMathNode(i);r&&this.append(r+";")}}parseMathNode(e){if(!e)return"";const s=e.localName;if(s==="apply")return this.parseApply(e);if(s==="ci")return e.textContent?.trim()||"";if(s==="cn"){const i=e.textContent?.trim()||"0",t=e.getAttributeNS(C,"units");return t?`${i} {units: ${t}}`:i}else{if(s==="piecewise")return this.parsePiecewise(e);if(s==="pi")return"pi";if(s==="bvar")return""}return console.log(`Unsupported MathML node: ${s}`),`/* Unsupported MathML node: ${s} */`}parseApply(e){const s=Array.from(e.children);if(s.length===0)return"";const i=s[0]?.localName,t=s.slice(1).map(r=>this.parseMathNode(r));switch(i){case"plus":return`(${t.join(" + ")})`;case"minus":return t.length===1?`-${t[0]}`:`(${t[0]} - ${t[1]})`;case"times":return`(${t.join(" * ")})`;case"divide":return`(${t[0]} / ${t[1]})`;case"eq":return`${t[0]} == ${t[1]}`;case"neq":return`${t[0]} != ${t[1]}`;case"lt":return`${t[0]} < ${t[1]}`;case"leq":return`${t[0]} <= ${t[1]}`;case"gt":return`${t[0]} > ${t[1]}`;case"geq":return`${t[0]} >= ${t[1]}`;case"and":return`${t.join(" and ")}`;case"or":return`${t.join(" or ")}`;case"diff":const r=s.find(l=>l.localName==="bvar"),c=s.find(l=>l.localName!=="diff"&&l.localName!=="bvar"),h=r?.children[0]?.textContent||"t";return`ode(${c?this.parseMathNode(c):"unknown"}, ${h})`;case"sin":case"cos":case"tan":case"exp":case"ln":case"log":return`${i}(${t[0]})`;case"root":return`sqrt(${t[0]})`;default:return`${i}(${t.join(", ")})`}}parsePiecewise(e){let s=[];return Array.from(e?.children||[]).forEach(t=>{if(t.localName==="piece"){const r=this.parseMathNode(t.children[0]),c=this.parseMathNode(t.children[1]);s.push(`${this.standardIndent}case ${c}: ${r};`)}else if(t.localName==="otherwise"){const r=this.parseMathNode(t.children[0]);s.push(`${this.standardIndent}otherwise: ${r};`)}}),`sel
|
|
8
8
|
${this.indent()}${s.join(`
|
|
9
9
|
${this.indent()}`)}
|
|
10
|
-
${this.indent()}endsel`}}class E{convert(
|
|
11
|
-
`);if(
|
|
10
|
+
${this.indent()}endsel`}}class E{convert(e){if(!e)return"";if(e.localName==="math")return Array.from(e.children).map(s=>this.convert(s)).join(`
|
|
11
|
+
`);if(e.localName==="apply"&&e.firstElementChild?.localName==="eq"){const s=Array.from(e.children),i=this.parseNode(s[1]),t=this.parseNode(s[2]);return`${i} = ${t}`}return this.parseNode(e)}parseNode(e){if(!e)return"";const s=e.localName;return s==="apply"?this.parseApply(e):s==="ci"?this.parseIdentifier(e.textContent||""):s==="cn"?e.textContent||"0":s==="piecewise"?this.parsePiecewise(e):""}escapeGreek(e){return["alpha","beta","gamma","delta","epsilon","zeta","eta","theta","iota","kappa","lambda","mu","nu","xi","omicron","pi","rho","sigma","tau","upsilon","phi","chi","psi","omega"].includes(e.toLowerCase())?`\\${e}`:e}parseIdentifier(e){if(!e.includes("_"))return this.escapeGreek(e);const s=e.split("_"),i=this.escapeGreek(s[0]||""),t=[];s[1]&&t.push(s[1]),s.length>4&&t.push(...s.slice(4));let r="";s.length===3&&(s[2]||[]).length===1?t.push(this.escapeGreek(s[2]||"")):s[2]&&(r=this.escapeGreek(s[2]),s[3]&&(r+=`_{${this.escapeGreek(s[3])}}`)),t.forEach((u,l)=>{t[l]=this.escapeGreek(u)});const c=t.join(",");let h=i;return c&&(h+=`_{${c}}`),r&&(h+=`^{${r}}`),h}parseApply(e){const s=Array.from(e?.children||[]),i=s[0]?.localName,t=s.slice(1).map(r=>this.parseNode(r));switch(i){case"plus":return t.join(" + ");case"minus":return t.length===1?`-${t[0]}`:`${t[0]} - ${t[1]}`;case"times":return t.join(" \\cdot ");case"divide":return`\\frac{${t[0]}}{${t[1]}}`;case"eq":return`${t[0]} == ${t[1]}`;case"neq":return`${t[0]} \\neq ${t[1]}`;case"lt":return`${t[0]} < ${t[1]}`;case"leq":return`${t[0]} \\leq ${t[1]}`;case"gt":return`${t[0]} > ${t[1]}`;case"geq":return`${t[0]} \\geq ${t[1]}`;case"and":return t.join(" \\land ");case"or":return t.join(" \\lor ");case"power":const r=s[1],c=t[0]||"",h=t[1];return r?.localName==="ci"||r?.localName==="cn"&&!c.trim().startsWith("-")?`{${c}}^{${h}}`:`\\left({${c}}\\right)^{${h}}`;case"root":case"sqrt":return`\\sqrt{${t[0]}}`;case"diff":const l=s.find(x=>x.localName==="bvar"),p=s.find(x=>x.localName!=="diff"&&x.localName!=="bvar"),d=l?this.parseNode(l.firstElementChild):"x";return`\\frac{d${p?this.parseNode(p):"y"}}{d${d}}`;case"exp":return`e^{${t[0]}}`;case"abs":return`\\left|${t[0]}\\right|`;case"floor":return`\\lfloor ${t[0]} \\rfloor`;case"ceil":return`\\lceil ${t[0]} \\rceil`;case"cos":case"cosh":case"log10":case"log":case"ln":case"max":case"min":case"sin":case"sinh":case"tan":case"tanh":return`\\${i}\\left(${t[0]}\\right)`;default:return console.log(`Unsupported MathML operator: ${i}`),`\\text{${i}}(${t.join(", ")})`}}parsePiecewise(e){let s="";return Array.from(e.children).forEach(t=>{if(t.localName==="piece"){const r=this.parseNode(t.children[0]),c=this.parseNode(t.children[1]);s+=`${r} & \\text{if } ${c} \\\\ `}else if(t.localName==="otherwise"){const r=this.parseNode(t.children[0]);s+=`${r} & \\text{otherwise}`}}),`\\begin{cases} ${s} \\end{cases}`}}m.CellMLLatexGenerator=E,m.CellMLTextGenerator=N,m.CellMLTextParser=g,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -28,8 +28,18 @@ export declare class CellMLTextParser {
|
|
|
28
28
|
private parseCondition;
|
|
29
29
|
private isComparisonToken;
|
|
30
30
|
private parseComparison;
|
|
31
|
+
/**
|
|
32
|
+
* Checks if an element is an <apply> block for a specific operator.
|
|
33
|
+
* e.g. isMathMLApply(node, 'plus') returns true for <apply><plus/>...</apply>
|
|
34
|
+
*/
|
|
35
|
+
private isMathMLApply;
|
|
31
36
|
private parseExpression;
|
|
32
37
|
private parseTerm;
|
|
38
|
+
/**
|
|
39
|
+
* Checks if the identifier is a reserved MathML constant name
|
|
40
|
+
* and returns the corresponding element, or null if it's a variable.
|
|
41
|
+
*/
|
|
42
|
+
private createMathMLConstant;
|
|
33
43
|
private parseFactor;
|
|
34
44
|
private parsePiecewise;
|
|
35
45
|
private parseFunctionCall;
|