# erm.js - The Esoteric Reducing Machine

erm.js creates composable machines for pattern matching.

```javascript
let monkeyKeystrokes =
    infiniteTypewriters(infiniteMonkeys).readToEnd()

// raw (data + location) with closures
match(...monkeyKeystrokes)(
  make(macbeth)(book => worksOfShakespeare.push(book)),
  make(twogentlemenofverona)(book => worksOfShakespeare.push(book)),
  make(ayorkshiretragedy)(haltAndCatchFire),
  _
)

// values with closures
match(...monkeyKeystrokes)(
  make(macbeth).stream(book => worksOfShakespeare.push(book)),
  make(twogentlemenofverona).stream(book => worksOfShakespeare.push(book)),
  make(ayorkshiretragedy)(haltAndCatchFire),
  _
)

// values to arrays
match(...monkeyKeystrokes)(
  make(macbeth).push(worksOfShakespeare),
  make(twogentlemenofverona).push(worksOfShakespeare),
  make(ayorkshiretragedy)(haltAndCatchFire),
  _
)
```

If like me you've got data, you've tried pattern matching with rxjs, you've tried finding arrays in arrays with the Knuth-Morris-Pratt algorithm, but it's all too much and not quite what you need, then maybe erm is the javascript pattern matching library you're looking for!

_note: this project is (pre) alpha right now and discussed APIs are subject to change_

## Introduction

erm.js will work with arrays of anything, including strings.

### Principle of Operation

- A _match$machine_ has one iterable input and has many _make$machines_.
- A _match$machine_ sends its iterable input in slices of _n(1,&#8734;)_ items to a _make$machine_ accepting _n_ arguments.
- When a _make$machine_ predicate returns _false_, the _match$machine_ restarts the slicing cycle sending input to the next _make$machine_ in the chain.
- A _match$machine_ will terminate when all input is accepted by the _make$machines_.
- A `_` machine is a _make$machine_ that accepts any input and always advances _match$machine_ 1 position.

### The static Match API

> __match__(_...input_)(__make__(_...predicate:unary|...value_)\[.__until__(_haltpredicate:unary|value_)](_output_, \[_error_])\[,...])

__match__ accepts _...input_ and returns a __match$machine__ - a callable object that accepts one or more make$machines

__make__ accepts _...predicate|...value_ and return a __make$machine__- a callable object that accepts an _output_ callback and optionally and _error_ callback.

#### Fixed Size Patterns

A __make$machine__ will be activated with the same number of items as _predicate_ has parameters *-or-* the same number of _...values_ provided; which is to say the _make$machine_ has the same arity; so a predicate `p => ...` will produce a machine that activates with 1 parameter, in this instance `p`; and the values `...['4', '2']` will produce a machine that activates with 2 parameters. `4` and `2`. Because of this, predicates with a `...rest` parameter are not compatible-. Variable length patterns can be matched using __make$machine.until__ - _à la Kleene star..._

#### Variable Size Patterns

The __make$machine__ also exposes an optional __until__ method which causes the machine to run again until the _haltpredicate_ signals true. To illustrate this with an albeit contrived example, compare it to the Regex \[^] and * operator:
```javascript
// regex
let username = /[^@]*/.match(emailaddress)
saveUsername(username)

// erm with predicates
match(emailaddress)(
  make(c => true).until(make(c => c == '@'))(output => saveUsername(output)),
  _
)

// erm with literals
match(emailaddress)(
  make(_).until('@')(output => saveUsername(output)),
  _
)
```

#### Predicates, Values and Callbacks

_predicate_ and _haltpredicate_ are as you would expect, p => true|false.

If a value _value_ is supplied to make it is automatically converted to _p => p == value_ i.e. `make(3.14) == make(p => p == 3.14)`

_output_ is your supplied callback function that is invoked with a single object `{ value, signal, location: { start, length } }`

_error_ is your optional callback function that is invoked with a single object `{ error, location: { start, length } }`

#### Utility Functions and Constants

__Match.not()__ will invert a predicate while preserving arity e.g. `let TRUE = make(p => true); let FALSE = make(not(TRUE))`

**Match.\_** is the 'unit' value symbol and acts as a wildcard when used in place of a make$machine. Like the `default:` label in a `switch` statement, `_` catches anything that your make$machines don't. Unlike the `default:` label in a switch statement, a match$machine without a `_` will not be able to read unmatched data and may not terminate.

## Quick Start

### Install
`npm install erm-js`

### Usage
The Match class exposes the basic building blocks `{ match, make, not, _ }` for composing machines:

```javascript
const { match, make, not, _  } = require('erm-js').Match

match(..."my input data") (
  make((i, n) => i == "i" && n == "n")(_in => console.log(_in)),
  _
)
```

### Examples
```javascript
const { match, make, not, _  } = require('erm-js').Match

// simple predicates
let h = p => p == 'h'
let e = p => p == 'e'
let l = p => p == 'l'
let o = p => p == 'o'
let z = p => p == 'z'

// still simple but more useful
let hello = (ph,pe,pl,pL,po) => h(ph) && e(pe) && l(pl) && l(pL) && o(po)

// tada!
match(...'hello world')(
  make(h)(x => console.log('h found:', x)),
  make(e)(x => console.log('e found:', x)),
  make(l)(x => console.log('l found:', x)),
  _
)

// kapow!
match(...'hello world')(
  make(hello)(x => console.log('hello found:', x)),
  _
)

// zorb!
match(...'hello world')(
  make(l).until(not(o))(x => console.log('ll found:', x)),
  _
)

// implementing a take-while function
let takewhile = input => predicate => {
  let items = []
  match(...input)(
    make(predicate).until(p => !predicate(p))(result => items.push(...result.value)).break(),
    _
  )
  return items
}

// and using it
let taken = takewhile(dumbpasswords)(p => p != "111111")

// implementing a partition by function
let partition = input => predicate => {
  let left = []
  let right = []
  match(...input)(
    make(predicate)(q => left.push(q.value)),
    make(p => !predicate(p))(q => right.push(q.value)),
    _
  )
  return [left, right]
}

// and using it
let [lefthandside, righthandside] = partition(dumbpasswords)(p => p.match(/^\d*$/))
```
