• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hetalang / heta-compiler / 26371141039

24 May 2026 07:51PM UTC coverage: 73.969% (+0.7%) from 73.305%
26371141039

push

github

metelkin
draft math-json

2023 of 2966 branches covered (68.21%)

Branch coverage included in aggregate %.

118 of 122 new or added lines in 3 files covered. (96.72%)

1 existing line in 1 file now uncovered.

3788 of 4890 relevant lines covered (77.46%)

156.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.98
/src/dynms/expression.js
1
const { Expression } = require('../core/expression');
2✔
2

3
const NUM_NAN = { num: 'NaN' };
2✔
4
const NUM_POSITIVE_INFINITY = { num: '+Infinity' };
2✔
5
const NUM_NEGATIVE_INFINITY = { num: '-Infinity' };
2✔
6

7
const OPERATOR_MAP = {
2✔
8
  larger: 'Greater',
9
  largerEq: 'GreaterEqual',
10
  smaller: 'Less',
11
  smallerEq: 'LessEqual',
12
  equal: 'Equal',
13
  unequal: 'NotEqual',
14
  and: 'And',
15
  or: 'Or',
16
  xor: 'Xor',
17
};
18

19
const FUNCTION_MAP = {
2✔
20
  abs: 'Abs',
21
  ceil: 'Ceil',
22
  cos: 'Cos',
23
  csc: 'Csc',
24
  cot: 'Cot',
25
  exp: 'Exp',
26
  factorial: 'Factorial',
27
  floor: 'Floor',
28
  log2: 'Lb',
29
  log10: 'Lg',
30
  max: 'Max',
31
  min: 'Min',
32
  sec: 'Sec',
33
  sign: 'Sign',
34
  sin: 'Sin',
35
  sqrt: 'Sqrt',
36
  tan: 'Tan',
37
  acos: 'Arccos',
38
  acot: 'Arccot',
39
  acsc: 'Arccsc',
40
  asec: 'Arcsec',
41
  asin: 'Arcsin',
42
  atan: 'Arctan',
43
};
44

45
function flattenAssociative(operator, operands) {
46
  const flat = [];
18✔
47

48
  for (const operand of operands) {
18✔
49
    const converted = _toMathJSON(operand);
38✔
50
    if (Array.isArray(converted) && converted[0] === operator) {
38✔
51
      flat.push(...converted.slice(1));
4✔
52
    } else {
53
      flat.push(converted);
34✔
54
    }
55
  }
56

57
  return [operator, ...flat];
18✔
58
}
59

60
function makeComparison(operator, args) {
61
  return [operator, ...args.map(_toMathJSON)];
13✔
62
}
63

64
function makeIf(operator, args) {
65
  const [left, right, whenTrue, whenFalse] = args;
5✔
66
  return ['If', [operator, _toMathJSON(left), _toMathJSON(right)], _toMathJSON(whenTrue), _toMathJSON(whenFalse)];
5✔
67
}
68

69
function piecewiseToMathJSON(args) {
70
  const hasOtherwise = args.length % 2 === 1;
4✔
71
  const otherwise = hasOtherwise ? _toMathJSON(args[args.length - 1]) : NUM_NAN;
4✔
72
  const pairs = hasOtherwise ? args.slice(0, -1) : args;
4✔
73

74
  if (pairs.length === 2) {
4✔
75
    return ['If', _toMathJSON(pairs[1]), _toMathJSON(pairs[0]), otherwise];
2✔
76
  }
77

78
  const items = ['Which'];
2✔
79
  for (let index = 0; index < pairs.length; index += 2) {
2✔
80
    items.push(_toMathJSON(pairs[index + 1]));
4✔
81
    items.push(_toMathJSON(pairs[index]));
4✔
82
  }
83
  items.push('True');
2✔
84
  items.push(otherwise);
2✔
85

86
  return items;
2✔
87
}
88

89
function functionToMathJSON(node) {
90
  const functionName = node.fn.name;
43✔
91
  const args = node.args;
43✔
92

93
  if (functionName === 'add') {
43✔
94
    return flattenAssociative('Add', args);
1✔
95
  }
96
  if (functionName === 'subtract') {
42✔
97
    return ['Add', _toMathJSON(args[0]), ['Negate', _toMathJSON(args[1])]];
1✔
98
  }
99
  if (functionName === 'multiply') {
41✔
100
    return flattenAssociative('Multiply', args);
1✔
101
  }
102
  if (functionName === 'divide') {
40✔
103
    return ['Divide', _toMathJSON(args[0]), _toMathJSON(args[1])];
1✔
104
  }
105
  if (functionName === 'pow') {
39✔
106
    return ['Power', _toMathJSON(args[0]), _toMathJSON(args[1])];
1✔
107
  }
108
  if (functionName === 'square') {
38✔
109
    return ['Square', _toMathJSON(args[0])];
1✔
110
  }
111
  if (functionName === 'cube') {
37✔
112
    return ['Power', _toMathJSON(args[0]), 3];
1✔
113
  }
114
  if (functionName === 'nthRoot') {
36✔
115
    return ['Root', _toMathJSON(args[0]), _toMathJSON(args[1])];
1✔
116
  }
117
  if (functionName === 'ln' || functionName === 'log') {
35✔
118
    return ['Ln', _toMathJSON(args[0])];
2✔
119
  }
120
  if (functionName === 'logbase') {
33✔
121
    return ['Log', _toMathJSON(args[0]), _toMathJSON(args[1])];
1✔
122
  }
123
  if (functionName === 'ifgt') {
32✔
124
    return makeIf('Greater', args);
1✔
125
  }
126
  if (functionName === 'ifge') {
31✔
127
    return makeIf('GreaterEqual', args);
1✔
128
  }
129
  if (functionName === 'iflt') {
30✔
130
    return makeIf('Less', args);
1✔
131
  }
132
  if (functionName === 'ifle') {
29✔
133
    return makeIf('LessEqual', args);
1✔
134
  }
135
  if (functionName === 'ifeq') {
28✔
136
    return makeIf('Equal', args);
1✔
137
  }
138
  if (functionName === 'piecewise') {
27✔
139
    return piecewiseToMathJSON(args);
4✔
140
  }
141
  if (FUNCTION_MAP[functionName]) {
23!
142
    return [FUNCTION_MAP[functionName], ...args.map(_toMathJSON)];
23✔
143
  }
144

NEW
145
  return [functionName, ...args.map(_toMathJSON)];
×
146
}
147

148
function _toMathJSON(node) {
149
  if (node.type === 'ParenthesisNode') {
247✔
150
    return _toMathJSON(node.content);
2✔
151
  }
152

153
  if (node.type === 'ConditionalNode') {
245✔
154
    return ['If', _toMathJSON(node.condition), _toMathJSON(node.trueExpr), _toMathJSON(node.falseExpr)];
1✔
155
  }
156

157
  if (node.type === 'ConstantNode') {
244✔
158
    if (typeof node.value === 'boolean') {
15✔
159
      return node.value ? 'True' : 'False';
2✔
160
    }
161
    if (Number.isNaN(node.value)) {
13✔
162
      return NUM_NAN;
1✔
163
    }
164
    if (node.value === Infinity) {
12✔
165
      return NUM_POSITIVE_INFINITY;
2✔
166
    }
167
    if (node.value === -Infinity) {
10!
NEW
168
      return NUM_NEGATIVE_INFINITY;
×
169
    }
170

171
    return node.value;
10✔
172
  }
173

174
  if (node.type === 'SymbolNode') {
229✔
175
    if (node.name === 'pi') {
143✔
176
      return 'Pi';
1✔
177
    }
178
    if (node.name === 'e') {
142✔
179
      return 'ExponentialE';
1✔
180
    }
181

182
    return node.name;
141✔
183
  }
184

185
  if (node.type === 'OperatorNode') {
86✔
186
    if (node.fn === 'unaryPlus') {
43✔
187
      return _toMathJSON(node.args[0]);
1✔
188
    }
189
    if (node.fn === 'unaryMinus') {
42✔
190
      const operand = _toMathJSON(node.args[0]);
4✔
191
      if (typeof operand === 'number') {
4✔
192
        return -operand;
1✔
193
      }
194
      if (operand && operand.num === '+Infinity') {
3✔
195
        return NUM_NEGATIVE_INFINITY;
1✔
196
      }
197
      if (operand && operand.num === 'NaN') {
2!
NEW
198
        return NUM_NAN;
×
199
      }
200
      return ['Negate', operand];
2✔
201
    }
202
    if (node.fn === 'add') {
38✔
203
      return flattenAssociative('Add', node.args);
4✔
204
    }
205
    if (node.fn === 'subtract') {
34✔
206
      return ['Add', _toMathJSON(node.args[0]), ['Negate', _toMathJSON(node.args[1])]];
1✔
207
    }
208
    if (node.fn === 'multiply') {
33✔
209
      return flattenAssociative('Multiply', node.args);
6✔
210
    }
211
    if (node.fn === 'divide') {
27✔
212
      return ['Divide', _toMathJSON(node.args[0]), _toMathJSON(node.args[1])];
4✔
213
    }
214
    if (node.fn === 'pow') {
23✔
215
      return ['Power', _toMathJSON(node.args[0]), _toMathJSON(node.args[1])];
3✔
216
    }
217
    if (node.fn === 'not') {
20✔
218
      return ['Not', _toMathJSON(node.args[0])];
1✔
219
    }
220
    if (OPERATOR_MAP[node.fn]) {
19!
221
      const mathJsonOperator = OPERATOR_MAP[node.fn];
19✔
222
      if (mathJsonOperator === 'And' || mathJsonOperator === 'Or' || mathJsonOperator === 'Xor') {
19✔
223
        return flattenAssociative(mathJsonOperator, node.args);
6✔
224
      }
225
      return makeComparison(mathJsonOperator, node.args);
13✔
226
    }
227
  }
228

229
  if (node.type === 'FunctionNode') {
43!
230
    return functionToMathJSON(node);
43✔
231
  }
232

NEW
233
  throw new TypeError(`Unsupported MathExpr node for MathJSON export: ${node.type}`);
×
234
}
235

236
Expression.prototype.toMathJSON = function() {
2✔
237
  return _toMathJSON(this.exprParsed);
81✔
238
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc