Skip to content

Commit

Permalink
Support passing additional arguments to Parser
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperisager committed Jun 23, 2020
1 parent 56664d6 commit 1ce3236
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 72 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Items that are related, such as breaking changes, new features, or changes to ex

## [Unreleased]

### Added

- [@siteimprove/alfa-parser](packages/alfa-parser): Additional arguments can now be defined for `Parser<I, T, E, A>` instances through a fourth type parameter, `A`.

## [0.2.0](../../compare/v0.1.0...v0.2.0) (2020-06-08)

### Breaking
Expand Down
152 changes: 80 additions & 72 deletions packages/alfa-parser/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,38 @@ import { Predicate } from "@siteimprove/alfa-predicate";
import { Ok, Result, Err } from "@siteimprove/alfa-result";
import { Thunk } from "@siteimprove/alfa-thunk";

export type Parser<I, T, E = never> = (input: I) => Result<readonly [I, T], E>;
export type Parser<I, T, E = never, A extends Array<unknown> = []> = (
input: I,
...args: A
) => Result<readonly [I, T], E>;

export namespace Parser {
export function map<I, T, U, E>(
parser: Parser<I, T, E>,
export function map<I, T, U, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>,
mapper: Mapper<T, U>
): Parser<I, U, E> {
return (input) =>
parser(input).map(
): Parser<I, U, E, A> {
return (input, ...args) =>
parser(input, ...args).map(
([remainder, value]) => [remainder, mapper(value)] as const
);
}

export function flatMap<I, T, U, E>(
parser: Parser<I, T, E>,
mapper: Mapper<T, Parser<I, U, E>>
): Parser<I, U, E> {
return (input) =>
parser(input).flatMap(([remainder, value]) => mapper(value)(remainder));
export function flatMap<I, T, U, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>,
mapper: Mapper<T, Parser<I, U, E, A>>
): Parser<I, U, E, A> {
return (input, ...args) =>
parser(input, ...args).flatMap(([remainder, value]) =>
mapper(value)(remainder, ...args)
);
}

export function filter<I, T, U extends T, E>(
parser: Parser<I, T, E>,
export function filter<I, T, U extends T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>,
predicate: Predicate<T, U>,
ifError: Thunk<E>
): Parser<I, U, E> {
return flatMap(parser, (value) => (input) => {
): Parser<I, U, E, A> {
return flatMap(parser, (value) => (input, ..._) => {
const result: Result<readonly [I, U], E> = predicate(value)
? Ok.of([input, value] as const)
: Err.of(ifError());
Expand All @@ -40,14 +45,14 @@ export namespace Parser {
});
}

export function zeroOrMore<I, T, E>(
parser: Parser<I, T, E>
): Parser<I, Iterable<T>, E> {
return (input) => {
export function zeroOrMore<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>
): Parser<I, Iterable<T>, E, A> {
return (input, ...args) => {
const values: Array<T> = [];

while (true) {
const result = parser(input);
const result = parser(input, ...args);

if (result.isOk()) {
const [remainder, value] = result.get();
Expand All @@ -63,23 +68,23 @@ export namespace Parser {
};
}

export function oneOrMore<I, T, E>(
parser: Parser<I, T, E>
): Parser<I, Iterable<T>, E> {
export function oneOrMore<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>
): Parser<I, Iterable<T>, E, A> {
return flatMap(parser, (head) =>
map(zeroOrMore(parser), (tail) => [head, ...tail])
);
}

export function take<I, T, E>(
parser: Parser<I, T, E>,
export function take<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>,
n: number
): Parser<I, Iterable<T>, E> {
return (input) => {
): Parser<I, Iterable<T>, E, A> {
return (input, ...args) => {
const values: Array<T> = [];

for (let i = 0; i < n; i++) {
const result = parser(input);
const result = parser(input, ...args);

if (result.isOk()) {
const [remainder, value] = result.get();
Expand All @@ -95,25 +100,28 @@ export namespace Parser {
};
}

export function peek<I, T, E>(parser: Parser<I, T, E>): Parser<I, T, E> {
return (input) => parser(input).map(([, value]) => [input, value]);
export function peek<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>
): Parser<I, T, E, A> {
return (input, ...args) =>
parser(input, ...args).map(([, value]) => [input, value]);
}

export function tee<I, T, E>(
parser: Parser<I, T, E>,
export function tee<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>,
callback: Callback<T>
): Parser<I, T, E> {
): Parser<I, T, E, A> {
return map(parser, (value) => {
callback(value);
return value;
});
}

export function option<I, T, E>(
parser: Parser<I, T, E>
): Parser<I, Option<T>, E> {
return (input) => {
const result = map(parser, (value) => Option.of(value))(input);
export function option<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>
): Parser<I, Option<T>, E, A> {
return (input, ...args) => {
const result = map(parser, (value) => Option.of(value))(input, ...args);

if (result.isOk()) {
return result;
Expand All @@ -123,66 +131,66 @@ export namespace Parser {
};
}

export function either<I, T, U, E>(
left: Parser<I, T, E>,
right: Parser<I, U, E>
): Parser<I, T | U, E> {
return (input) => {
const result = left(input);
export function either<I, T, U, E, A extends Array<unknown>>(
left: Parser<I, T, E, A>,
right: Parser<I, U, E, A>
): Parser<I, T | U, E, A> {
return (input, ...args) => {
const result = left(input, ...args);

if (result.isOk()) {
return result;
}

return right(input);
return right(input, ...args);
};
}

export function pair<I, T, U, E>(
left: Parser<I, T, E>,
right: Parser<I, U, E>
): Parser<I, [T, U], E> {
export function pair<I, T, U, E, A extends Array<unknown>>(
left: Parser<I, T, E, A>,
right: Parser<I, U, E, A>
): Parser<I, [T, U], E, A> {
return flatMap(left, (left) => map(right, (right) => [left, right]));
}

export function left<I, T, U, E>(
left: Parser<I, T, E>,
right: Parser<I, U, E>
): Parser<I, T, E> {
export function left<I, T, U, E, A extends Array<unknown>>(
left: Parser<I, T, E, A>,
right: Parser<I, U, E, A>
): Parser<I, T, E, A> {
return flatMap(left, (left) => map(right, () => left));
}

export function right<I, T, U, E>(
left: Parser<I, T, E>,
right: Parser<I, U, E>
): Parser<I, U, E> {
export function right<I, T, U, E, A extends Array<unknown>>(
left: Parser<I, T, E, A>,
right: Parser<I, U, E, A>
): Parser<I, U, E, A> {
return flatMap(left, () => map(right, (right) => right));
}

export function delimited<I, T, E>(
left: Parser<I, unknown, E>,
separator: Parser<I, T, E>,
right: Parser<I, unknown, E> = left
): Parser<I, T, E> {
export function delimited<I, T, E, A extends Array<unknown>>(
left: Parser<I, unknown, E, A>,
separator: Parser<I, T, E, A>,
right: Parser<I, unknown, E, A> = left
): Parser<I, T, E, A> {
return flatMap(left, () =>
flatMap(separator, (separator) => map(right, () => separator))
);
}

export function separated<I, T, U, E>(
left: Parser<I, T, E>,
separator: Parser<I, unknown, E>,
right: Parser<I, U, E>
): Parser<I, [T, U], E> {
export function separated<I, T, U, E, A extends Array<unknown>>(
left: Parser<I, T, E, A>,
separator: Parser<I, unknown, E, A>,
right: Parser<I, U, E, A>
): Parser<I, [T, U], E, A> {
return flatMap(left, (left) =>
flatMap(separator, () => map(right, (right) => [left, right]))
);
}

export function separatedList<I, T, E>(
parser: Parser<I, T, E>,
separator: Parser<I, unknown, E>
): Parser<I, Iterable<T>, E> {
export function separatedList<I, T, E, A extends Array<unknown>>(
parser: Parser<I, T, E, A>,
separator: Parser<I, unknown, E, A>
): Parser<I, Iterable<T>, E, A> {
return map(
pair(parser, zeroOrMore(right(separator, parser))),
([first, rest]) => [first, ...rest]
Expand Down

0 comments on commit 1ce3236

Please sign in to comment.