Skip to content

Commit 404b11a

Browse files
authored
Merge pull request #71 from pie-framework/fix/PD-4848
fix: add support for unit multiplication and unit comparison PD-4848
2 parents c6e5950 + f92259b commit 404b11a

4 files changed

Lines changed: 112 additions & 8 deletions

File tree

docs/js/demo.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39217,15 +39217,23 @@ function create(factories, config) {
3921739217
const mathjs = create(all, { number: "Fraction" });
3921839218
mathjs.replacer;
3921939219

39220+
function _optionalChain$4(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
3922039221
const log$3 = logger("mv:ast-to-math");
3922139222
const m$6 = mathjs;
3922239223
const operators = {
3922339224
"+": function (operands) {
3922439225
return new m$6.OperatorNode("+", "add", operands);
3922539226
},
3922639227
"*": function (operands) {
39227-
if (operands[1] && operands[1].isUnit) {
39228-
return m$6.multiply(operands[0].value, operands[1]);
39228+
const [left, right] = operands;
39229+
if (
39230+
_optionalChain$4([left, 'optionalAccess', _2 => _2.type]) === "ConstantNode" &&
39231+
_optionalChain$4([right, 'optionalAccess', _3 => _3.isUnit]) &&
39232+
typeof left.value === "object"
39233+
) {
39234+
const leftVal = left.value.valueOf();
39235+
const unitResult = m$6.multiply(leftVal, right);
39236+
return unitResult;
3922939237
}
3923039238
return new m$6.OperatorNode("*", "multiply", operands);
3923139239
},
@@ -40268,6 +40276,27 @@ const normalize = (a) => {
4026840276
const isMathEqual$1 = (a, b) => {
4026940277
let as;
4027040278
let bs;
40279+
if (_optionalChain([a, 'optionalAccess', _ => _.isUnit]) && _optionalChain([b, 'optionalAccess', _2 => _2.isUnit])) {
40280+
try {
40281+
return m$1.equal(a, b);
40282+
} catch (e) {
40283+
log$1(
40284+
"[isMathEqual] Error comparing units:",
40285+
_optionalChain([a, 'optionalAccess', _3 => _3.toString, 'optionalCall', _4 => _4()]),
40286+
_optionalChain([b, 'optionalAccess', _5 => _5.toString, 'optionalCall', _6 => _6()]),
40287+
e
40288+
);
40289+
return false;
40290+
}
40291+
}
40292+
if (_optionalChain([a, 'optionalAccess', _7 => _7.isUnit]) || _optionalChain([b, 'optionalAccess', _8 => _8.isUnit])) {
40293+
log$1(
40294+
"[isMathEqual] Mismatched unit comparison:",
40295+
_optionalChain([a, 'optionalAccess', _9 => _9.toString, 'optionalCall', _10 => _10()]),
40296+
_optionalChain([b, 'optionalAccess', _11 => _11.toString, 'optionalCall', _12 => _12()])
40297+
);
40298+
return false;
40299+
}
4027140300
as = a.conditionals ? normalize(a) : sort(normalize(a));
4027240301
bs = b.conditionals ? normalize(b) : sort(normalize(b));
4027340302
log$1("[isMathEqual]", as.toString(), "==?", bs.toString());
@@ -40291,9 +40320,9 @@ const isMathEqual$1 = (a, b) => {
4029140320
return compareEquations(as, bs, true);
4029240321
}
4029340322
if (
40294-
_optionalChain([as, 'optionalAccess', _ => _.conditionals, 'optionalAccess', _2 => _2.length]) === _optionalChain([bs, 'optionalAccess', _3 => _3.conditionals, 'optionalAccess', _4 => _4.length]) &&
40295-
_optionalChain([as, 'optionalAccess', _5 => _5.conditionals, 'optionalAccess', _6 => _6.length]) === 2 &&
40296-
_optionalChain([as, 'optionalAccess', _7 => _7.conditionals, 'optionalAccess', _8 => _8.toString, 'call', _9 => _9()]) === _optionalChain([bs, 'optionalAccess', _10 => _10.conditionals, 'optionalAccess', _11 => _11.toString, 'call', _12 => _12()])
40323+
_optionalChain([as, 'optionalAccess', _13 => _13.conditionals, 'optionalAccess', _14 => _14.length]) === _optionalChain([bs, 'optionalAccess', _15 => _15.conditionals, 'optionalAccess', _16 => _16.length]) &&
40324+
_optionalChain([as, 'optionalAccess', _17 => _17.conditionals, 'optionalAccess', _18 => _18.length]) === 2 &&
40325+
_optionalChain([as, 'optionalAccess', _19 => _19.conditionals, 'optionalAccess', _20 => _20.toString, 'call', _21 => _21()]) === _optionalChain([bs, 'optionalAccess', _22 => _22.conditionals, 'optionalAccess', _23 => _23.toString, 'call', _24 => _24()])
4029740326
) {
4029840327
const params = [
4029940328
"smaller",

src/conversion/ast-to-mathjs.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@ const operators = {
2929
return new m.OperatorNode("+", "add", operands);
3030
},
3131
"*": function (operands) {
32-
if (operands[1] && operands[1].isUnit) {
33-
return m.multiply(operands[0].value, operands[1]);
32+
const [left, right] = operands;
33+
if (
34+
left?.type === "ConstantNode" &&
35+
right?.isUnit &&
36+
typeof left.value === "object"
37+
) {
38+
const leftVal = left.value.valueOf();
39+
const unitResult = m.multiply(leftVal, right);
40+
41+
return unitResult;
3442
}
3543
return new m.OperatorNode("*", "multiply", operands);
3644
},
@@ -167,7 +175,7 @@ export class AstToMathJs {
167175
this.convert(operands[1]),
168176
]);
169177

170-
const f = operands[0]
178+
const f = operands[0];
171179
const args = operands[1];
172180
let f_args;
173181

@@ -184,6 +192,7 @@ export class AstToMathJs {
184192

185193
if (operator === "unit") {
186194
const unit = new m.Unit(1, operands[0]);
195+
187196
return unit;
188197
}
189198

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
export default {
2+
mode: "symbolic",
3+
tests: [
4+
{
5+
// simple expression with unit
6+
target: "3.5 \\text{gal}",
7+
eq: ["3.5 \\text{gal}", "\\frac{7}{2} \\text{gal}"],
8+
ne: [
9+
"3.5",
10+
"3.5 \\text{L}", // liters ≠ gallons
11+
],
12+
},
13+
{
14+
// multiplication with units
15+
target: "2 * 3.5 \\text{gal}",
16+
eq: ["7 \\text{gal}", "2 * \\frac{7}{2} \\text{gal}"],
17+
ne: ["7", "7 \\text{L}"],
18+
},
19+
{
20+
// addition with same unit
21+
target: "1 \\text{gal} + 2 \\text{gal}",
22+
eq: ["3 \\text{gal}", "\\text{gal} + 2 \\text{gal}"],
23+
ne: ["3", "1 + 2 \\text{gal}", "1 \\text{L} + 2 \\text{gal}"],
24+
},
25+
{
26+
// TODO test unit in equation
27+
// target: "x = 4 gal",
28+
// eq: ["4 gal = x"],
29+
// ne: ["x = 4", "x = 4 L"],
30+
},
31+
{
32+
target: "4 gal",
33+
eq: ["4 gal"],
34+
ne: ["4", "4 L"],
35+
},
36+
{
37+
// units in division
38+
target: "\\frac{7 \\text{gal}}{2}",
39+
eq: ["3.5 \\text{gal}"],
40+
ne: ["3.5", "\\frac{7}{2}"],
41+
},
42+
],
43+
};

src/symbolic/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,29 @@ export const isMathEqual = (a: any, b: any) => {
172172
let as: MathNode;
173173
let bs: MathNode;
174174

175+
if (a?.isUnit && b?.isUnit) {
176+
try {
177+
return m.equal(a, b);
178+
} catch (e) {
179+
log(
180+
"[isMathEqual] Error comparing units:",
181+
a?.toString?.(),
182+
b?.toString?.(),
183+
e
184+
);
185+
return false;
186+
}
187+
}
188+
189+
if (a?.isUnit || b?.isUnit) {
190+
log(
191+
"[isMathEqual] Mismatched unit comparison:",
192+
a?.toString?.(),
193+
b?.toString?.()
194+
);
195+
return false;
196+
}
197+
175198
// apply sort if we are not in a relationalNode
176199
as = a.conditionals ? normalize(a) : sort(normalize(a));
177200

0 commit comments

Comments
 (0)