diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d1887e..4314030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice. +# 0.6.8 + +- **New Feature** + - Add "do notation" to `Parser` monad (@CYBAI) + # 0.6.7 - **New Feature** diff --git a/docs/modules/Parser.ts.md b/docs/modules/Parser.ts.md index f378069..a39f201 100644 --- a/docs/modules/Parser.ts.md +++ b/docs/modules/Parser.ts.md @@ -65,6 +65,9 @@ Added in v0.6.0 - [parser](#parser) - [model](#model) - [Parser (interface)](#parser-interface) +- [utils](#utils) + - [bind](#bind) + - [bindTo](#bindto) --- @@ -637,3 +640,28 @@ export interface Parser { ``` Added in v0.6.0 + +# utils + +## bind + +**Signature** + +```ts +export declare const bind: ( + name: Exclude, + f: (a: A) => Parser +) => (fa: Parser) => Parser +``` + +Added in v0.6.8 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: (name: N) => (fa: Parser) => Parser +``` + +Added in v0.6.8 diff --git a/examples/command.ts b/examples/command.ts index 78905dd..45a8dab 100644 --- a/examples/command.ts +++ b/examples/command.ts @@ -33,7 +33,6 @@ * For example, one could generalize the parser exemplified below to accept any command. Then, the * structure of the parsed AST for specific commands can be enforced using instances of `io-ts` `Schema`s. */ -import { Do } from 'fp-ts-contrib/lib/Do' import * as A from 'fp-ts/lib/Array' import { mapLeft, Either } from 'fp-ts/lib/Either' import { getStructMonoid, Monoid } from 'fp-ts/lib/Monoid' @@ -173,10 +172,11 @@ const positional: P.Parser = pipe(C.many1(C.notSpace), P.map const argument = P.either(flag, () => P.either(named, () => positional)) const statement = (cmd: string) => - Do(P.parser) - .bind('command', whitespaceSurrounded(S.string(cmd))) - .bind('args', P.many(whitespaceSurrounded(argument))) - .done() + pipe( + whitespaceSurrounded(S.string(cmd)), + P.bindTo('command'), + P.bind('args', () => P.many(whitespaceSurrounded(argument))) + ) const ast = (command: string, source: string): P.Parser => { return pipe( diff --git a/package-lock.json b/package-lock.json index b75e5a6..0016055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parser-ts", - "version": "0.6.6", + "version": "0.6.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2226,12 +2226,6 @@ "integrity": "sha512-oz8L+EZiztqGVLhgdL+63b4hpfdJkEKH/4vRoS/AuUwYqmn7vHAzWMu1jtoYfB0pPxo5UioWVT2XOuftyivUSQ==", "dev": true }, - "fp-ts-contrib": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/fp-ts-contrib/-/fp-ts-contrib-0.1.17.tgz", - "integrity": "sha512-UBQ7AdGp0GktLkIn3e6npVIfLm1ZM+racizgVqL88j6HBLBoBjRtVjIxRIS6YbNypuP94ngaZpy2SEib9lnZ9A==", - "dev": true - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", diff --git a/package.json b/package.json index 829de1d..8c6569b 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "docs-ts": "^0.5.1", "dtslint": "^0.4.2", "fp-ts": "^2.0.0", - "fp-ts-contrib": "^0.1.17", "import-path-rewrite": "github:gcanti/import-path-rewrite", "jest": "^24.8.0", "mocha": "^5.2.0", diff --git a/src/Parser.ts b/src/Parser.ts index abb49d4..573cc4b 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -577,3 +577,41 @@ export const parser: Monad2 & Alternative2 = { alt: alt_, zero: fail } + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @internal + */ +const bind_ = ( + a: A, + name: Exclude, + b: B +): { [K in keyof A | N]: K extends keyof A ? A[K] : B } => Object.assign({}, a, { [name]: b }) as any + +/** + * @since 0.6.8 + */ +export const bindTo = (name: N) => (fa: Parser): Parser => + pipe( + fa, + map(a => bind_({}, name, a)) + ) + +/** + * @since 0.6.8 + */ +export const bind = (name: Exclude, f: (a: A) => Parser) => ( + fa: Parser +): Parser => + pipe( + fa, + chain(a => + pipe( + f(a), + map(b => bind_(a, name, b)) + ) + ) + ) diff --git a/test/Parser.ts b/test/Parser.ts index 4b7fd45..1ac251f 100644 --- a/test/Parser.ts +++ b/test/Parser.ts @@ -3,6 +3,7 @@ import { char as C, parser as P, string as S } from '../src' import { error, success } from '../src/ParseResult' import { stream } from '../src/Stream' import { run } from './helpers' +import { pipe } from 'fp-ts/lib/pipeable' describe('Parser', () => { it('eof', () => { @@ -120,4 +121,25 @@ describe('Parser', () => { assert.deepStrictEqual(run(parser, 'ab'), success(['a', 'b'], stream(['a', 'b'], 2), stream(['a', 'b']))) assert.deepStrictEqual(run(parser, 'abc'), success(['a', 'b'], stream(['a', 'b', 'c'], 2), stream(['a', 'b', 'c']))) }) + + it('bind', () => { + const parser = pipe( + C.char('a'), + P.bindTo('a'), + P.bind('b', () => C.char('b')), + P.map(({ a, b }) => [a, b]) + ) + assert.deepStrictEqual(run(parser, 'ab'), success(['a', 'b'], stream(['a', 'b'], 2), stream(['a', 'b']))) + assert.deepStrictEqual(run(parser, 'bc'), error(stream(['b', 'c'], 0), ['"a"'])) + }) + + it('bindTo', () => { + const parser = pipe( + C.char('a'), + P.bindTo('headingA'), + P.map(({ headingA }) => [headingA]) + ) + assert.deepStrictEqual(run(parser, 'ab'), success(['a'], stream(['a', 'b'], 1), stream(['a', 'b']))) + assert.deepStrictEqual(run(parser, 'bc'), error(stream(['b', 'c'], 0), ['"a"'])) + }) })