@teachinglab/omd 0.3.7 → 0.3.8
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.
- package/package.json +1 -1
- package/src/omdEquation.js +24 -18
- package/src/omdExpression.js +7 -0
- package/src/omdFunction.js +5 -3
- package/src/omdString.js +12 -1
- package/src/omdUtils.js +84 -0
- package/src/omdVariable.js +1 -0
package/package.json
CHANGED
package/src/omdEquation.js
CHANGED
|
@@ -8,6 +8,7 @@ import { omdExpression } from "./omdExpression.js";
|
|
|
8
8
|
import { omdVariable } from "./omdVariable.js";
|
|
9
9
|
import { omdString } from "./omdString.js";
|
|
10
10
|
import { omdNumber } from "./omdNumber.js";
|
|
11
|
+
import { parseEquationString } from "./omdUtils.js";
|
|
11
12
|
|
|
12
13
|
export class omdEquation extends omdMetaExpression
|
|
13
14
|
{
|
|
@@ -50,7 +51,7 @@ export class omdEquation extends omdMetaExpression
|
|
|
50
51
|
else if ( left.omdType == "number" ) this.leftExpression = new omdNumber();
|
|
51
52
|
else if ( left.omdType == "variable" ) this.leftExpression = new omdVariable();
|
|
52
53
|
else if ( left.omdType == "term" ) this.leftExpression = new omdTerm();
|
|
53
|
-
else if ( left.omdType == "string" || typeof left == 'string' ) this.leftExpression = new
|
|
54
|
+
else if ( left.omdType == "string" || typeof left == 'string' ) this.leftExpression = new omdExpression();
|
|
54
55
|
|
|
55
56
|
if (this.leftExpression && typeof this.leftExpression.loadFromJSON === 'function' && left && typeof left === 'object') {
|
|
56
57
|
this.leftExpression.loadFromJSON( left );
|
|
@@ -69,7 +70,7 @@ export class omdEquation extends omdMetaExpression
|
|
|
69
70
|
else if ( right.omdType == "number" ) this.rightExpression = new omdNumber();
|
|
70
71
|
else if ( right.omdType == "variable" ) this.rightExpression = new omdVariable();
|
|
71
72
|
else if ( right.omdType == "term" ) this.rightExpression = new omdTerm();
|
|
72
|
-
else if ( right.omdType == "string" || typeof right == 'string' ) this.rightExpression = new
|
|
73
|
+
else if ( right.omdType == "string" || typeof right == 'string' ) this.rightExpression = new omdExpression();
|
|
73
74
|
|
|
74
75
|
if (this.rightExpression && typeof this.rightExpression.loadFromJSON === 'function' && right && typeof right === 'object') {
|
|
75
76
|
this.rightExpression.loadFromJSON( right );
|
|
@@ -81,23 +82,28 @@ export class omdEquation extends omdMetaExpression
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
// If no structured left/right provided but an `equation` string exists, parse it into
|
|
85
|
+
// If no structured left/right provided but an `equation` string exists, try to parse it into structured expressions
|
|
85
86
|
if ( (!this.leftExpression || !this.rightExpression) && typeof data.equation === 'string' ) {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this.
|
|
87
|
+
const parsed = parseEquationString(data.equation);
|
|
88
|
+
if ( parsed ) {
|
|
89
|
+
if (!this.leftExpression) this.leftExpression = new omdExpression();
|
|
90
|
+
if (!this.rightExpression) this.rightExpression = new omdExpression();
|
|
91
|
+
// pass parsed structured JSON to expressions
|
|
92
|
+
if (this.leftExpression && typeof this.leftExpression.loadFromJSON === 'function') this.leftExpression.loadFromJSON(parsed.leftExpression);
|
|
93
|
+
if (this.rightExpression && typeof this.rightExpression.loadFromJSON === 'function') this.rightExpression.loadFromJSON(parsed.rightExpression);
|
|
94
|
+
this.leftHolder.removeAllChildren(); this.leftHolder.addChild(this.leftExpression);
|
|
95
|
+
this.rightHolder.removeAllChildren(); this.rightHolder.addChild(this.rightExpression);
|
|
96
|
+
} else {
|
|
97
|
+
// fallback: treat sides as plain strings
|
|
98
|
+
const parts = String(data.equation || '').split('=');
|
|
99
|
+
const leftStr = (parts[0] || '').trim();
|
|
100
|
+
const rightStr = (parts[1] || '').trim();
|
|
101
|
+
if (!this.leftExpression) this.leftExpression = new omdString(leftStr || '');
|
|
102
|
+
if (!this.rightExpression) this.rightExpression = new omdString(rightStr || '');
|
|
103
|
+
if (this.leftExpression && typeof this.leftExpression.loadFromJSON === 'function' && typeof leftStr === 'string') this.leftExpression.loadFromJSON(leftStr);
|
|
104
|
+
if (this.rightExpression && typeof this.rightExpression.loadFromJSON === 'function' && typeof rightStr === 'string') this.rightExpression.loadFromJSON(rightStr);
|
|
105
|
+
this.leftHolder.removeAllChildren(); this.leftHolder.addChild(this.leftExpression);
|
|
106
|
+
this.rightHolder.removeAllChildren(); this.rightHolder.addChild(this.rightExpression);
|
|
101
107
|
}
|
|
102
108
|
}
|
|
103
109
|
|
package/src/omdExpression.js
CHANGED
|
@@ -7,6 +7,7 @@ import { omdOperator } from "./omdOperator.js";
|
|
|
7
7
|
import { omdNumber } from "./omdNumber.js";
|
|
8
8
|
import { omdVariable } from "./omdVariable.js";
|
|
9
9
|
import { omdMetaExpression } from "./omdMetaExpression.js"
|
|
10
|
+
import { parseExpressionString } from "./omdUtils.js";
|
|
10
11
|
|
|
11
12
|
export class omdExpression extends omdMetaExpression
|
|
12
13
|
{
|
|
@@ -29,6 +30,12 @@ export class omdExpression extends omdMetaExpression
|
|
|
29
30
|
|
|
30
31
|
loadFromJSON( data )
|
|
31
32
|
{
|
|
33
|
+
// Accept either structured object or a plain expression string
|
|
34
|
+
if ( typeof data === 'string' ) {
|
|
35
|
+
const parsed = parseExpressionString(data);
|
|
36
|
+
if ( parsed ) data = parsed;
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
if ( typeof data.termsAndOpers != "undefined" )
|
|
33
40
|
{
|
|
34
41
|
for( var i=0; i<data.termsAndOpers.length; i++ )
|
package/src/omdFunction.js
CHANGED
|
@@ -64,8 +64,9 @@ export class omdFunction extends omdMetaExpression
|
|
|
64
64
|
|
|
65
65
|
// format the f(x) part on the left
|
|
66
66
|
this.leftHolder.removeAllChildren();
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
var funcText = this.functionName + "(" + this.inputVariableArray[0] + ")"; // this should be refactored as a function call
|
|
68
|
+
this.leftExpression = new omdExpression();
|
|
69
|
+
this.leftExpression.loadFromJSON(funcText);
|
|
69
70
|
this.leftHolder.addChild( this.leftExpression );
|
|
70
71
|
|
|
71
72
|
this.updateLayout();
|
|
@@ -75,7 +76,8 @@ export class omdFunction extends omdMetaExpression
|
|
|
75
76
|
{
|
|
76
77
|
this.leftHolder.removeAllChildren();
|
|
77
78
|
var funcText = funcName + "(" + funcVariable + ")"; // this should be refactored as a function call
|
|
78
|
-
this.leftExpression = new
|
|
79
|
+
this.leftExpression = new omdExpression();
|
|
80
|
+
this.leftExpression.loadFromJSON(funcText);
|
|
79
81
|
this.leftHolder.addChild( this.leftExpression );
|
|
80
82
|
|
|
81
83
|
this.rightExpresion = rightExp;
|
package/src/omdString.js
CHANGED
|
@@ -9,7 +9,7 @@ export class omdString extends omdMetaExpression
|
|
|
9
9
|
// initialization
|
|
10
10
|
super();
|
|
11
11
|
|
|
12
|
-
this.type = "
|
|
12
|
+
this.type = "omdString";
|
|
13
13
|
|
|
14
14
|
this.numText = new jsvgTextBox();
|
|
15
15
|
this.numText.setWidthAndHeight( 30,30 );
|
|
@@ -36,4 +36,15 @@ export class omdString extends omdMetaExpression
|
|
|
36
36
|
|
|
37
37
|
this.setWidthAndHeight( this.backRect.width, this.backRect.height );
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
loadFromJSON(data) {
|
|
41
|
+
if (typeof data === 'string') {
|
|
42
|
+
this.setName(data);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (data && typeof data.name === 'string') {
|
|
46
|
+
this.setName(data.name);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
39
50
|
}
|
package/src/omdUtils.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Small parsers to convert simple equation/expression strings into OMD JSON
|
|
2
|
+
|
|
3
|
+
// Parse an expression like "3x+4-2y" into a termsAndOpers array:
|
|
4
|
+
// ['3x', '+', '4', '-', '2y'] -> [{omdType:'term', coefficient:3, variable:'x' }, {omdType:'operator', operator:'+'}, ...]
|
|
5
|
+
export function parseExpressionString(str) {
|
|
6
|
+
const cleaned = String(str || '').replace(/\s+/g, '');
|
|
7
|
+
if (!cleaned) return null;
|
|
8
|
+
|
|
9
|
+
// Tokenize numbers, variables, operators, and superscripts
|
|
10
|
+
// Token regex covers +/- as signs attached to numbers/vars, or standalone operators
|
|
11
|
+
const tokenRe = /([+-]?\d*\.?\d+[a-zA-Z]*)|([+-]?[a-zA-Z]+\^?\d*)|([+\-*/=×÷])/g;
|
|
12
|
+
const matches = cleaned.match(tokenRe) || [];
|
|
13
|
+
if (matches.length === 0) return null;
|
|
14
|
+
|
|
15
|
+
const out = [];
|
|
16
|
+
for (let tok of matches) {
|
|
17
|
+
// operator-only tokens
|
|
18
|
+
if (/^[+\-*/=×÷]$/.test(tok)) {
|
|
19
|
+
out.push({ omdType: 'operator', operator: tok.replace('*', '×') });
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// term or number/variable
|
|
23
|
+
const m = tok.match(/^([+-]?)(\d*\.?\d*)([a-zA-Z]?)(?:\^(\d+))?$/);
|
|
24
|
+
if (m) {
|
|
25
|
+
const sign = m[1] === '-' ? -1 : 1;
|
|
26
|
+
const coefRaw = m[2];
|
|
27
|
+
const varChar = m[3] || '';
|
|
28
|
+
const exp = m[4] ? Number(m[4]) : 1;
|
|
29
|
+
let coef = 1;
|
|
30
|
+
if (coefRaw && coefRaw.length > 0) coef = Number(coefRaw);
|
|
31
|
+
if (!varChar && coefRaw) {
|
|
32
|
+
// pure number
|
|
33
|
+
out.push({ omdType: 'number', value: sign * coef });
|
|
34
|
+
} else {
|
|
35
|
+
out.push({ omdType: 'term', coefficient: sign * coef, variable: varChar, exponent: exp });
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
// fallback to string token
|
|
39
|
+
out.push({ omdType: 'string', name: tok });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If first token is an operator like '+' or '-', and followed by a term/number, fold it
|
|
44
|
+
const folded = [];
|
|
45
|
+
for (let i = 0; i < out.length; i++) {
|
|
46
|
+
const t = out[i];
|
|
47
|
+
if (t.omdType === 'operator' && (t.operator === '+' || t.operator === '-')) {
|
|
48
|
+
// If it's a leading sign attached to next term, merge
|
|
49
|
+
const next = out[i+1];
|
|
50
|
+
if (next && (next.omdType === 'term' || next.omdType === 'number')) {
|
|
51
|
+
if (next.omdType === 'number') {
|
|
52
|
+
next.value = (t.operator === '-') ? -next.value : next.value;
|
|
53
|
+
} else if (next.omdType === 'term') {
|
|
54
|
+
next.coefficient = (t.operator === '-') ? -next.coefficient : next.coefficient;
|
|
55
|
+
}
|
|
56
|
+
i++; // skip next because we've merged
|
|
57
|
+
folded.push(next);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
folded.push(t);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Interleave operators where missing: if folded array alternates term/number and operator
|
|
65
|
+
const result = [];
|
|
66
|
+
for (let i = 0; i < folded.length; i++) {
|
|
67
|
+
result.push(folded[i]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { termsAndOpers: result };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Parse an equation like "3x+4=2x+1" into left/right expression JSON
|
|
74
|
+
export function parseEquationString(str) {
|
|
75
|
+
const s = String(str || '');
|
|
76
|
+
const parts = s.split('=');
|
|
77
|
+
if (parts.length !== 2) return null;
|
|
78
|
+
const left = parts[0].trim();
|
|
79
|
+
const right = parts[1].trim();
|
|
80
|
+
const leftJson = parseExpressionString(left);
|
|
81
|
+
const rightJson = parseExpressionString(right);
|
|
82
|
+
if (!leftJson || !rightJson) return null;
|
|
83
|
+
return { leftExpression: leftJson, rightExpression: rightJson };
|
|
84
|
+
}
|