## swift-math-parser

### 3.0.0

Math expression parser built with Point•Free's swift-parsing package

### Everything, Everywhere, All at Once

###### 2022-12-12T21:57:05Z

Better detection of implied multiplication involving combine symbols.

# swift-math-parser

Basic math expression parser built with Point•Free's swift-parsing package (v0.11.0).

```let parser = MathParser()
let evaluator = parser.parse('4 × sin(t × π) + 2 × sin(t × π)')
evaluator.eval("t", value: 0.0) // => 0.0
evaluator.eval("t", value: 0.25) // => 4.2426406871192848
evaluator.eval("t", value: 0.5) // => 6
evaluator.eval("t", value: 1.0) // => 0```

The parser will return `nil` if it is unable to completely parse the expression.

By default, the expression parser and evaluator handle the following symbols and functions:

• Constants: `pi` (`π`) and `e`
• 1-argument functions: `sin`, `cos`, `tan`, `log10`, `ln` (`loge`), `log2`, `exp`, `ceil`, `floor`, `round`, `sqrt` (`√`)
• 2-argument functions: `atan`, `hypot`, `pow` 1
• alternative math operator symbols: `×` for multiplication and `÷` for division (see example above for use of `×`)

You can reference additional symbols or variables and functions by providing your own mapping functions. There are two places where this can be done:

• `MathParser.init`
• `MathParser.Evaluator.eval`

If a symbol or function does not exist during an `eval` call, the final result will be `NaN`. If a symbol is resolved during parsing, it will be replaced with the symbol's value. Otherwise, it will be resolved during a future `eval` call. Same for function calls -- if the function is known during parsing and all arguments have a known value, then it will be replaced with the function result. Otherwise, the function call will take place during an `eval` call.

Example:

```let mySymbols = ["foo": 123.4]
let myFuncs: [String:(Double)->Double] = ["twice": {\$0 + \$0}]
let parser = MathParser(symbols: mySymbols.producer, unaryFunctions: myFuncs.producer)
let myEvalFuncs: [String:(Double)->Double] = ["power": {\$0 * \$0}]
let evaluator = parser.parse("power(twice(foo))")

# Expression parsed and `twice(foo)` resolved to `246.8` but `power` is still unknown
evaluator?.value // => nan

# Give evaluator way to resolve `power(246.8)`
evaluator?.eval(unaryFunctions: myEvalFuncs.producer) // => 60910.240000000005```

## Implied Multiplication

One of the original goals of this parser was to be able to accept a Wolfram Alpha math expression more or less as-is -- for instance the definition https://www.wolframalpha.com/input/?i=Sawsbuck+Winter+Form%E2%80%90like+curve -- without any editing. Here is the start of the textual representation from the above link:

``````x(t) = ((-2/9 sin(11/7 - 4 t) + 78/11 sin(t + 11/7) + 2/7 sin(2 t + 8/5) ...
``````

Skipping over the assignment one can readily see that the representation includes implied multiplication between terms when there are no explicit math operators present (eg `-2/9` x `sin(11/7 - 4` x `t)`). There is support for this sort of operation in the parser that can be enabled by setting `enableImpliedMultiplication` when creating a new `MathParser` instance (it defaults to `false`). Note that when enabled, an expression such as `2^3 2^4` would be considered a valid expression, resolving to `2^3 * 2^4 = 128`, and `4sin(t(pi))` would become `4 * sin(t * pi)`.

Here is the original example expression with implied multiplication:

```let parser = MathParser(enableImpliedMultiplication: true)
let evaluator = parser.parse("4sin(t π) + 2sin(t π)")
evaluator.eval("t", value: 0.0) // => 0.0
evaluator.eval("t", value: 0.25) // => 4.2426406871192848
evaluator.eval("t", value: 0.5) // => 6
evaluator.eval("t", value: 1.0) // => 0```

Note that with `enableImpliedMultiplication` enabled, `tπ` will be broken into `t` and `π` even though one could have a symbol called `tπ`. This eager splitting of a symbol may cause unexpected multiplication. For instance, if you have a symbol defined with the name `pin`, this will be split into `pi` and `n` because `pi` / `π` is a known symbol. The best way to protect from this happening is to not enable `enabledImpliedMultiplication` but an alternative is to always separate individual symbols with a space -- or place one inside a pair of parentheses.

## Footnotes

1. Redundant since there is already the `^` operator.

### Description

• Swift Tools 5.5.0

### Dependencies

Last updated: Sun Jan 22 2023 18:24:57 GMT-0500 (GMT-05:00)