@teachinglab/omd 0.3.6 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -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 omdString( typeof left == 'string' ? left : left.name || '' );
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 omdString( typeof right == 'string' ? right : right.name || '' );
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 simple string nodes
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 eq = data.equation || '';
87
- const parts = eq.split('=');
88
- const leftStr = (parts[0] || '').trim();
89
- const rightStr = (parts[1] || '').trim();
90
-
91
- if (!this.leftExpression) this.leftExpression = new omdString(leftStr || '');
92
- if (!this.rightExpression) this.rightExpression = new omdString(rightStr || '');
93
-
94
- if (this.leftExpression) {
95
- this.leftHolder.removeAllChildren();
96
- this.leftHolder.addChild(this.leftExpression);
97
- }
98
- if (this.rightExpression) {
99
- this.rightHolder.removeAllChildren();
100
- this.rightHolder.addChild(this.rightExpression);
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
 
@@ -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++ )
@@ -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
- var funcText = this.functionName + "(" + this.inputVariableArray[0] + ")"; // this should be refactored as a function call
68
- this.leftExpression = new omdString(funcText);
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 omdString(funcText);
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
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { omdColor } from "./omdColor.js";
3
3
  import { omdMetaExpression } from "./omdMetaExpression.js"
4
-
4
+ import { jsvgTextBox } from "@teachinglab/jsvg";
5
5
  export class omdString extends omdMetaExpression
6
6
  {
7
7
  constructor( V = 'string' )
@@ -9,7 +9,7 @@ export class omdString extends omdMetaExpression
9
9
  // initialization
10
10
  super();
11
11
 
12
- this.type = "omdVariable";
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
  }
@@ -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
+ }
@@ -1,6 +1,7 @@
1
1
 
2
2
  import { omdColor } from "./omdColor.js";
3
3
  import { omdMetaExpression } from "./omdMetaExpression.js"
4
+ import { jsvgTextBox } from "@teachinglab/jsvg";
4
5
 
5
6
  export class omdVariable extends omdMetaExpression
6
7
  {