(expression: string, additionalSymbols?: Object)
| 21 | } |
| 22 | |
| 23 | function calculate(expression: string, additionalSymbols?: Object): number { |
| 24 | const symbolMap = mergeSymbolMaps(additionalSymbols) |
| 25 | |
| 26 | let match |
| 27 | const operators = [symbolMap.symbols['('].prefix] |
| 28 | const values = [] |
| 29 | |
| 30 | const pattern = new RegExp( // Pattern for numbers |
| 31 | `\\d+(?:\\.\\d+)?|${ |
| 32 | // ...and patterns for individual operators/function names |
| 33 | Object.keys(symbolMap.symbols) |
| 34 | .map(key => symbolMap.symbols[key]) |
| 35 | // longer symbols should be listed first |
| 36 | // $FlowFixMe |
| 37 | .sort((a, b) => b.symbol.length - a.symbol.length) |
| 38 | // $FlowFixMe |
| 39 | .map(val => val.regSymbol) |
| 40 | .join('|') |
| 41 | }|(\\S)`, |
| 42 | 'g', |
| 43 | ) |
| 44 | pattern.lastIndex = 0 // Reset regular expression object |
| 45 | |
| 46 | let afterValue = false |
| 47 | |
| 48 | do { |
| 49 | match = pattern.exec(expression) |
| 50 | |
| 51 | const [token, bad] = match || [')', undefined] |
| 52 | const notNumber = symbolMap.symbols[token] |
| 53 | const notNewValue = notNumber && !notNumber.prefix && !notNumber.func |
| 54 | const notAfterValue = !notNumber || (!notNumber.postfix && !notNumber.infix) |
| 55 | |
| 56 | // Check for syntax errors: |
| 57 | if (bad || (afterValue ? notAfterValue : notNewValue)) { |
| 58 | throw new PolishedError(37, match ? match.index : expression.length, expression) |
| 59 | } |
| 60 | |
| 61 | if (afterValue) { |
| 62 | // We either have an infix or postfix operator (they should be mutually exclusive) |
| 63 | const curr = notNumber.postfix || notNumber.infix |
| 64 | do { |
| 65 | const prev = operators[operators.length - 1] |
| 66 | if ((curr.precedence - prev.precedence || prev.rightToLeft) > 0) break |
| 67 | // Apply previous operator, since it has precedence over current one |
| 68 | } while (exec(operators, values)) // Exit loop after executing an opening parenthesis or function |
| 69 | afterValue = curr.notation === 'postfix' |
| 70 | if (curr.symbol !== ')') { |
| 71 | operators.push(curr) |
| 72 | // Postfix always has precedence over any operator that follows after it |
| 73 | if (afterValue) exec(operators, values) |
| 74 | } |
| 75 | } else if (notNumber) { |
| 76 | // prefix operator or function |
| 77 | operators.push(notNumber.prefix || notNumber.func) |
| 78 | if (notNumber.func) { |
| 79 | // Require an opening parenthesis |
| 80 | match = pattern.exec(expression) |
no test coverage detected
searching dependent graphs…