Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/modules/Parser.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,10 @@ Matches the provided parser `p` that occurs between the provided `left` and `rig
**Signature**

```ts
export declare const between: <I, A>(left: Parser<I, A>, right: Parser<I, A>) => <B>(p: Parser<I, B>) => Parser<I, B>
export declare const between: <I, A, B = A>(
left: Parser<I, A>,
right: Parser<I, B>
) => <C>(p: Parser<I, C>) => Parser<I, C>
```

Added in v0.6.4
Expand Down Expand Up @@ -487,7 +490,7 @@ run(parser, 'a')
// { _tag: 'Right', right: { _tag: 'Some', value: 'a' } }

run(parser, 'b')
// { _tag: 'Left', left: { _tag: 'None' } }
// { _tag: 'Right', right: { _tag: 'None' } }
```

Added in v0.6.10
Expand Down
14 changes: 14 additions & 0 deletions docs/modules/char.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Added in v0.6.0
- [upper](#upper)
- [constructors](#constructors)
- [char](#char)
- [charC](#charc)
- [notChar](#notchar)
- [notOneOf](#notoneof)
- [oneOf](#oneof)
Expand Down Expand Up @@ -213,6 +214,19 @@ export declare const char: (c: Char) => P.Parser<Char, Char>

Added in v0.6.0

## charC

The `charC` parser constructor returns a parser which matches only the
specified single character, case-insensitive

**Signature**

```ts
export declare const charC: (c: Char) => P.Parser<Char, Char>
```

Added in v0.6.15

## notChar

The `notChar` parser constructor makes a parser which will match any
Expand Down
61 changes: 57 additions & 4 deletions docs/modules/string.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ Added in v0.6.0
- [spaces1](#spaces1)
- [constructors](#constructors)
- [oneOf](#oneof)
- [oneOfC](#oneofc)
- [string](#string)
- [stringC](#stringc)
- [destructors](#destructors)
- [fold](#fold)

Expand Down Expand Up @@ -171,14 +173,53 @@ Matches one of a list of strings.
**Signature**

```ts
export declare function oneOf<F extends URIS>(
F: Functor1<F> & Foldable1<F>
): (ss: Kind<F, string>) => P.Parser<C.Char, string>
export declare function oneOf<F>(F: Functor<F> & Foldable<F>): (ss: HKT<F, string>) => P.Parser<C.Char, string>
export declare const oneOf: {
<
U extends
| 'Option'
| 'ReadonlyRecord'
| 'Eq'
| 'Ord'
| 'NonEmptyArray'
| 'Array'
| 'ReadonlyNonEmptyArray'
| 'ReadonlyArray'
>(
F: Functor1<U> & Foldable1<U>
): (ss: Kind<U, string>) => P.Parser<C.Char, string>
<U>(F: Functor<U> & Foldable<U>): (ss: HKT<U, string>) => P.Parser<C.Char, string>
}
```

Added in v0.6.0

## oneOfC

Matches one of a list of strings, case-insensitive.

**Signature**

```ts
export declare const oneOfC: {
<
U extends
| 'Option'
| 'ReadonlyRecord'
| 'Eq'
| 'Ord'
| 'NonEmptyArray'
| 'Array'
| 'ReadonlyNonEmptyArray'
| 'ReadonlyArray'
>(
F: Functor1<U> & Foldable1<U>
): (ss: Kind<U, string>) => P.Parser<C.Char, string>
<U>(F: Functor<U> & Foldable<U>): (ss: HKT<U, string>) => P.Parser<C.Char, string>
}
```

Added in v0.6.15

## string

Matches the exact string provided.
Expand All @@ -191,6 +232,18 @@ export declare const string: (s: string) => P.Parser<C.Char, string>

Added in v0.6.0

## stringC

Matches the exact string provided, case-insensitive

**Signature**

```ts
export declare const stringC: (s: string) => P.Parser<C.Char, string>
```

Added in v0.6.15

# destructors

## fold
Expand Down
56 changes: 33 additions & 23 deletions examples/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ export const Named = (name: string, value: string): Named => ({ _tag: 'Named', n

export const Positional = (value: string): Positional => ({ _tag: 'Positional', value })

export const FlagArg = (value: string): Args => ({
flags: [value],
export const FlagArg = (values: string): Args => ({
flags: values.split(''),
named: R.empty,
positional: A.empty
})
Expand Down Expand Up @@ -164,7 +164,7 @@ const flag: P.Parser<string, Flag> = pipe(
const named: P.Parser<string, Named> = pipe(
doubleDash,
P.chain(() => P.sepBy1(equals, identifier)),
P.map(([name, value]) => Named(name, value))
P.map(([name, ...values]) => Named(name, values.join('=')))
)

const positional: P.Parser<string, Positional> = pipe(C.many1(C.notSpace), P.map(Positional))
Expand All @@ -189,34 +189,44 @@ const ast = (command: string, source: string): P.Parser<string, Ast> => {
)
}

const parseCommand = <E>(cmd: string, onLeft: (cmd: string) => E) => (source: string): Either<E, Ast> =>
pipe(
run(ast(cmd, source), source),
mapLeft(() => onLeft(cmd))
)
const parseCommand = <E>(cmd: string, onLeft: (error: string) => E) => (source: string): Either<E, Ast> =>
pipe(run(ast(cmd, source), source), mapLeft(onLeft))

const cmd = 'foo'
const source = 'foo ./bar -b --baz=qux'
// tslint:disable-next-line: no-console
const command = parseCommand('foo', e => console.error(e))

// tslint:disable-next-line: no-console
console.log(JSON.stringify(parseCommand(cmd, c => console.error(`command not found: ${c}`))(source), null, 2))
console.log(JSON.stringify(command('foo ./bar -bd --baz=qux=45 --tar=get --t'), null, 2))
/*
{
_tag: 'Right',
right: {
command: 'foo',
source: 'foo ./bar -b --baz=qux',
arguments: {
flags: [
"b"
"_tag": "Right",
"right": {
"command": "foo",
"source": "foo ./bar -b --baz=qux=45 --tar=get --t",
"args": {
"flags": [
"b",
"d"
],
named: {
baz: "qux",
"named": {
"baz": "qux=45",
"tar": "get",
"t": ""
},
positional: [
"./bar",
],
"positional": [
"./bar"
]
}
}
}
*/

// tslint:disable-next-line: no-console
console.log(JSON.stringify(command('bar ./bar -bd --baz=qux=45 --tar=get --t'), null, 2))
/*
> 1 | bar ./bar -bd --baz=qux=45 --tar=get --t
| ^ Expected: "foo"
{
"_tag": "Left"
}
*/
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions src/ParseResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { empty, getMonoid } from 'fp-ts/lib/Array'
import { Either, left, right } from 'fp-ts/lib/Either'
import { getFirstSemigroup, getLastSemigroup, getStructSemigroup, Semigroup } from 'fp-ts/lib/Semigroup'
import { getFirstSemigroup, semigroupAny, getStructSemigroup, Semigroup } from 'fp-ts/lib/Semigroup'
import { Stream } from './Stream'

// -------------------------------------------------------------------------------------
Expand Down Expand Up @@ -103,12 +103,11 @@ export const extend = <I>(err1: ParseError<I>, err2: ParseError<I>): ParseError<

const getSemigroup = <I>(): Semigroup<ParseError<I>> => ({
concat: (x, y) => {
if (x.input.cursor < y.input.cursor) return getLastSemigroup<ParseError<I>>().concat(x, y)
if (x.input.cursor > y.input.cursor) return getFirstSemigroup<ParseError<I>>().concat(x, y)

if (x.input.cursor < y.input.cursor) return y
if (x.input.cursor > y.input.cursor) return x
return getStructSemigroup<ParseError<I>>({
input: getFirstSemigroup<Stream<I>>(),
fatal: getFirstSemigroup<boolean>(),
fatal: semigroupAny,
expected: getMonoid<string>()
}).concat(x, y)
}
Expand Down
21 changes: 9 additions & 12 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ export const either: <I, A>(p: Parser<I, A>, f: () => Parser<I, A>) => Parser<I,
return e
}
return pipe(
f()(i),
i,
f(),
E.mapLeft(err => extend(e.left, err))
)
}
Expand Down Expand Up @@ -265,13 +266,11 @@ export const many1: <I, A>(p: Parser<I, A>) => Parser<I, NEA.NonEmptyArray<A>> =
* @category combinators
* @since 0.6.0
*/
export const sepBy = <I, A, B>(sep: Parser<I, A>, p: Parser<I, B>): Parser<I, Array<B>> => {
const nil: Parser<I, Array<B>> = of(A.empty)
return pipe(
export const sepBy = <I, A, B>(sep: Parser<I, A>, p: Parser<I, B>): Parser<I, Array<B>> =>
pipe(
sepBy1(sep, p),
alt(() => nil)
alt(() => of<I, Array<B>>(A.empty))
)
}

/**
* Matches the provided parser `p` one or more times, but requires the
Expand Down Expand Up @@ -353,7 +352,7 @@ export const filter: {
* @category combinators
* @since 0.6.4
*/
export const between: <I, A>(left: Parser<I, A>, right: Parser<I, A>) => <B>(p: Parser<I, B>) => Parser<I, B> = (
export const between: <I, A, B = A>(left: Parser<I, A>, right: Parser<I, B>) => <C>(p: Parser<I, C>) => Parser<I, C> = (
left,
right
) => p =>
Expand Down Expand Up @@ -431,7 +430,7 @@ export const takeUntil: <I>(predicate: Predicate<I>) => Parser<I, Array<I>> = pr
* // { _tag: 'Right', right: { _tag: 'Some', value: 'a' } }
*
* run(parser, 'b')
* // { _tag: 'Left', left: { _tag: 'None' } }
* // { _tag: 'Right', right: { _tag: 'None' } }
*
* @category combinators
* @since 0.6.10
Expand Down Expand Up @@ -532,9 +531,7 @@ const map_: Monad2<URI>['map'] = (ma, f) => i =>
const ap_: Monad2<URI>['ap'] = (mab, ma) => chain_(mab, f => map_(ma, f))
const chain_: Chain2<URI>['chain'] = (ma, f) => seq(ma, f)
const chainRec_: ChainRec2<URI>['chainRec'] = <I, A, B>(a: A, f: (a: A) => Parser<I, E.Either<A, B>>): Parser<I, B> => {
const split = (start: Stream<I>) => (
result: ParseSuccess<I, E.Either<A, B>>
): E.Either<Next<I, A>, ParseResult<I, B>> =>
const split = (start: Stream<I>, result: ParseSuccess<I, E.Either<A, B>>): E.Either<Next<I, A>, ParseResult<I, B>> =>
E.isLeft(result.value)
? E.left({ value: result.value.left, stream: result.next })
: E.right(success(result.value.right, result.next, start))
Expand All @@ -544,7 +541,7 @@ const chainRec_: ChainRec2<URI>['chainRec'] = <I, A, B>(a: A, f: (a: A) => Parse
if (E.isLeft(result)) {
return E.right(error(state.stream, result.left.expected, result.left.fatal))
}
return split(start)(result.right)
return split(start, result.right)
})
}
const alt_: Alt2<URI>['alt'] = (fa, that) => either(fa, that)
Expand Down
10 changes: 10 additions & 0 deletions src/char.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ export const char: (c: Char) => P.Parser<Char, Char> = c =>
`"${c}"`
)

/**
* The `charC` parser constructor returns a parser which matches only the
* specified single character, case-insensitive
*
* @category constructors
* @since 0.6.15
*/
export const charC: (c: Char) => P.Parser<Char, Char> = c =>
P.either(char(c.toLowerCase()), () => char(c.toUpperCase()))

/**
* The `notChar` parser constructor makes a parser which will match any
* single character other than the one provided.
Expand Down
Loading