@fw-components/formula-editor 2.0.7-formula-editor.22 → 2.0.7-formula-editor.24

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.
@@ -78,9 +78,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
78
78
  if (Boolean(recommendation)) {
79
79
  this.recommendations = [];
80
80
  this.currentCursorPosition = parseOutput.newCursorPosition;
81
- /**
82
- * update cursor position in text area
83
- */
81
+ /* update cursor position in text area */
84
82
  setTimeout(() => {
85
83
  this.editor.setSelectionRange(this.currentCursorPosition, this.currentCursorPosition);
86
84
  }, 0);
@@ -11,8 +11,8 @@ let SuggestionMenu = class SuggestionMenu extends LitElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.recommendations = [];
14
- this.onClickRecommendation = (recommendation) => { };
15
- this._selectedRecommendationIndex = 0;
14
+ this.onClickRecommendation = () => { };
15
+ this._selectedRecommendationIndex = -1;
16
16
  }
17
17
  scrollToSelectedRecommendation(index) {
18
18
  const listItem = this.suggestionList?.querySelectorAll("li")[index];
@@ -1,4 +1,5 @@
1
1
  export const mathematicalOperators = new Set(["^", "+", "-", "*", "/"]);
2
+ export const unaryOperators = ["+", "-"];
2
3
  export const operatorPrecedence = {
3
4
  "^": 3,
4
5
  "/": 2,
@@ -3,14 +3,19 @@ import { Recommender } from "./recommendor.js";
3
3
  import { Stack } from "./stack.js";
4
4
  import { Queue } from "./queue.js";
5
5
  import { Expectation } from "../types";
6
- import { mathematicalOperators, operatorPrecedence } from "./constants.js";
6
+ import { mathematicalOperators, operatorPrecedence, unaryOperators } from "./constants.js";
7
7
  export class Parser {
8
8
  constructor(variables, minSuggestionLen) {
9
9
  this.variables = variables;
10
10
  this._recommender = new Recommender(Array.from(this.variables.keys()), minSuggestionLen);
11
11
  }
12
+ getFormulaTokens(formulaString) {
13
+ if (!formulaString?.length)
14
+ return [];
15
+ return formulaString.match(/'[^']*'|\d+|[A-Za-z_][A-Za-z0-9_]*|[-+(),*^/:?\s]/g);
16
+ }
12
17
  parseInput(formula, prevCurPos = null, recommendation = null) {
13
- const tokens = formula.match(/'[^']*'|\d+|[A-Za-z_][A-Za-z0-9_]*|[-+(),*^/:?\s]/g);
18
+ const tokens = this.getFormulaTokens(formula);
14
19
  const parentheses = new Stack();
15
20
  let expectation = Expectation.VARIABLE;
16
21
  let currentPosition = 0;
@@ -80,7 +85,7 @@ export class Parser {
80
85
  * No error for Unary `+` and `-` as they might represent a positive or negative number respectively
81
86
  */
82
87
  else if (expectation === Expectation.VARIABLE && !isNumber && !isSpace && token != "("
83
- && !((token === "-" || token === "+") && (!parsedString.trim() || previousToken === "(" || mathematicalOperators.has(previousToken)))) {
88
+ && !((unaryOperators.includes(token)) && (!parsedString.trim() || previousToken === "(" || mathematicalOperators.has(previousToken)))) {
84
89
  parseOutput.errorString = `Expected variable/number at position ${currentPosition}`;
85
90
  expectation = Expectation.UNDEFINED;
86
91
  }
@@ -150,20 +155,16 @@ export class Parser {
150
155
  return parseOutput;
151
156
  }
152
157
  buildRPN(formula) {
153
- if (this.parseInput(formula).errorString) {
158
+ if (this.parseInput(formula).errorString)
154
159
  return null;
155
- }
156
- const tokens = formula
157
- .match(/'[^']*'|\d+|[A-Za-z_][A-Za-z0-9_]*|[-+(),*^/:?\s]/g)
158
- ?.filter((el) => !/\s+/.test(el) && el !== "");
159
- // Handling the special case of unary `-` and `+`.
160
+ const tokens = this.getFormulaTokens(formula)?.filter((el) => !/\s+/.test(el) && el !== "");
160
161
  let previousToken = "";
161
162
  let carriedToken = null;
162
163
  const parsedTokens = [];
163
164
  let currentTokens = "";
165
+ // Check if variables include unary operators `-` and `+`.
164
166
  for (const token of tokens) {
165
- if ((token === "+" || token === "-") &&
166
- (!currentTokens.trim() || previousToken === "(" || mathematicalOperators.has(previousToken))) {
167
+ if ((unaryOperators.includes(token)) && (!currentTokens.trim() || previousToken === "(" || mathematicalOperators.has(previousToken))) {
167
168
  carriedToken = token;
168
169
  }
169
170
  else if (carriedToken) {
@@ -192,9 +193,7 @@ export class Parser {
192
193
  operatorStack.pop();
193
194
  }
194
195
  else if (mathematicalOperators.has(token)) {
195
- while (mathematicalOperators.has(operatorStack.top()) &&
196
- operatorPrecedence[token] <=
197
- operatorPrecedence[operatorStack.top()]) {
196
+ while (mathematicalOperators.has(operatorStack.top()) && operatorPrecedence[token] <= operatorPrecedence[operatorStack.top()]) {
198
197
  outputQueue.enqueue(operatorStack.pop());
199
198
  }
200
199
  operatorStack.push(token);
@@ -210,87 +209,67 @@ export class Parser {
210
209
  }
211
210
  addParentheses(formula) {
212
211
  const rpn = this.buildRPN(formula);
213
- if (!rpn) {
212
+ if (!rpn)
214
213
  return null;
215
- }
216
214
  const lexedRPN = [];
217
215
  while (!rpn.isEmpty()) {
218
216
  lexedRPN.push(rpn.dequeue());
219
217
  }
220
- // Stores the operators that we encounter in the RPN
221
- let operatorStack = new Stack();
222
- // Stores the `results`, which are essentially individual groups
223
- // of tokens showing a meaningful value.
224
- let resultStack = new Stack();
218
+ const operatorStack = new Stack();
219
+ const resultStack = new Stack();
225
220
  lexedRPN.forEach((symbol) => {
226
- let parsedLeftExpression, parsedRightExpression;
227
- // If we encounter a number or a variable in the RPN, it is itself
228
- // a calculated entity (say a result in itself), needs no modification
229
- // and can be directly put into the result stack.
230
- if (((symbol[0] === "+" || symbol[0] === "-") && this.variables.has(symbol.substring(1))) ||
221
+ let parsedLeftExpression;
222
+ let parsedRightExpression;
223
+ // check if the symbol is a number or variable or unaryOperatorPreceded Variable
224
+ if (((unaryOperators.includes(symbol[0])) && this.variables.has(symbol.substring(1))) ||
231
225
  this.variables.has(symbol) ||
232
- (!isNaN(parseFloat(symbol)) && isFinite(parseFloat(symbol)))) {
226
+ (!Number.isNaN(parseFloat(symbol)) && Number.isFinite(parseFloat(symbol)))) {
233
227
  resultStack.push(symbol);
234
228
  operatorStack.push(null);
235
229
  }
236
- // If it is not a number/variable then it is an operator. We will
237
- // take out previous operators from the `operatorStack`, compare
238
- // them with the current one, adds brackets accordingly to the `results`
239
- // around it, and then finally add it to the `operatorStack` for
240
- // future reference.
230
+ // If symbol is an operator, check operatorStack, adds brackets accordingly to the result and add it to operatorStack
241
231
  else if (Object.keys(operatorPrecedence).includes(symbol)) {
242
- let [rightExpression, leftExpression, operatorA, operatorB] = [
232
+ const [rightExpression, leftExpression, operatorA, operatorB] = [
243
233
  resultStack.pop(),
244
234
  resultStack.pop(),
245
235
  operatorStack.pop(),
246
236
  operatorStack.pop(),
247
237
  ];
248
- // The conditions that govern when to show a parenthesis.
249
- if (operatorPrecedence[operatorB] <=
250
- operatorPrecedence[symbol] ||
251
- (operatorPrecedence[operatorB] ===
252
- operatorPrecedence[symbol] &&
253
- ["/", "-"].includes(symbol))) {
238
+ if ((operatorPrecedence[operatorB] <= operatorPrecedence[symbol]) ||
239
+ (operatorPrecedence[operatorB] === operatorPrecedence[symbol] && ["/", "-"].includes(symbol))) {
254
240
  parsedLeftExpression = `(${leftExpression})`;
255
241
  }
256
242
  else {
257
243
  parsedLeftExpression = leftExpression;
258
244
  }
259
- if (operatorPrecedence[operatorA] <=
260
- operatorPrecedence[symbol] ||
261
- (operatorPrecedence[operatorA] ===
262
- operatorPrecedence[symbol] &&
263
- ["/", "-"].includes(symbol))) {
245
+ if (operatorPrecedence[operatorA] <= operatorPrecedence[symbol] ||
246
+ (operatorPrecedence[operatorA] === operatorPrecedence[symbol] && ["/", "-"].includes(symbol))) {
264
247
  parsedRightExpression = `(${rightExpression})`;
265
248
  }
266
249
  else {
267
250
  parsedRightExpression = rightExpression;
268
251
  }
269
- // The bracket included expression is now itself a `result`
270
252
  resultStack.push(`${parsedLeftExpression} ${symbol} ${parsedRightExpression}`);
271
253
  operatorStack.push(symbol);
272
254
  }
273
255
  else
274
256
  throw `${symbol} is not a recognized symbol`;
275
257
  });
276
- if (!resultStack.isEmpty()) {
277
- return resultStack.pop();
278
- }
279
- else
258
+ if (resultStack.isEmpty())
280
259
  throw `${lexedRPN} is not a correct RPN`;
260
+ return resultStack.pop();
281
261
  }
282
262
  calculate(formula) {
283
- let rpn = this.buildRPN(formula);
284
- let calculationResult = {
263
+ const formulaRPN = this.buildRPN(formula);
264
+ const calculationResult = {
285
265
  result: undefined,
286
266
  errorString: null,
287
267
  };
288
- if (!rpn) {
268
+ if (!formulaRPN)
289
269
  return calculationResult;
290
- }
291
- let calcStack = new Stack();
292
- while (!rpn.isEmpty()) {
293
- const frontItem = rpn.dequeue();
270
+ const calcStack = new Stack();
271
+ while (!formulaRPN.isEmpty()) {
272
+ const frontItem = formulaRPN.dequeue();
294
273
  if (!mathematicalOperators.has(frontItem)) {
295
274
  const [sign, variableKey] = /^[+-]/.test(frontItem) ? [frontItem[0], frontItem.slice(1)] : ["", frontItem];
296
275
  const operandValue = Number.parseFloat(this.variables.get(variableKey)?.toString() ?? variableKey);
@@ -298,9 +277,9 @@ export class Parser {
298
277
  calcStack.push(Big(number));
299
278
  }
300
279
  else {
301
- let operator = frontItem;
302
- let numB = calcStack.pop();
303
- let numA = calcStack.pop();
280
+ const operator = frontItem;
281
+ const numB = calcStack.pop();
282
+ const numA = calcStack.pop();
304
283
  try {
305
284
  switch (operator) {
306
285
  case "+":
@@ -319,8 +298,6 @@ export class Parser {
319
298
  }
320
299
  calcStack.push(Big(numA).div(Big(numB)));
321
300
  break;
322
- // Big.js doesn't support exponentiating a Big to a Big, which
323
- // is obvious due to performance overheads. Use this case with care.
324
301
  case "^":
325
302
  calcStack.push(Big(numA).pow(parseFloat(Big(numB).toString())));
326
303
  }
@@ -17,6 +17,8 @@ export class Queue {
17
17
  return element;
18
18
  }
19
19
  peek() {
20
+ if (this.isEmpty())
21
+ throw new Error("Cannot peek an empty queue");
20
22
  return this._elements[this._head];
21
23
  }
22
24
  isEmpty() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fw-components/formula-editor",
3
- "version": "2.0.7-formula-editor.22",
3
+ "version": "2.0.7-formula-editor.24",
4
4
  "description": "A WYSIWYG type formula editor",
5
5
  "main": "dist/formula-editor/src/formula-editor.js",
6
6
  "exports": {
@@ -31,5 +31,5 @@
31
31
  "@types/big.js": "^6.1.6",
32
32
  "es-dev-server": "^2.1.0"
33
33
  },
34
- "gitHead": "daf2f4613926ce2aaef670decf920e336e5b4781"
34
+ "gitHead": "2b46a14d01bdeb5ed11a92252082ae8cb73a3d62"
35
35
  }