diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f113f7b3a..2c97cdfc20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` instances through a fourth type parameter, `A`. + ## [0.2.0](../../compare/v0.1.0...v0.2.0) (2020-06-08) ### Breaking diff --git a/packages/alfa-parser/src/parser.ts b/packages/alfa-parser/src/parser.ts index 1623421f5b..0bb6af9fbc 100644 --- a/packages/alfa-parser/src/parser.ts +++ b/packages/alfa-parser/src/parser.ts @@ -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 = (input: I) => Result; +export type Parser = []> = ( + input: I, + ...args: A +) => Result; export namespace Parser { - export function map( - parser: Parser, + export function map>( + parser: Parser, mapper: Mapper - ): Parser { - return (input) => - parser(input).map( + ): Parser { + return (input, ...args) => + parser(input, ...args).map( ([remainder, value]) => [remainder, mapper(value)] as const ); } - export function flatMap( - parser: Parser, - mapper: Mapper> - ): Parser { - return (input) => - parser(input).flatMap(([remainder, value]) => mapper(value)(remainder)); + export function flatMap>( + parser: Parser, + mapper: Mapper> + ): Parser { + return (input, ...args) => + parser(input, ...args).flatMap(([remainder, value]) => + mapper(value)(remainder, ...args) + ); } - export function filter( - parser: Parser, + export function filter>( + parser: Parser, predicate: Predicate, ifError: Thunk - ): Parser { - return flatMap(parser, (value) => (input) => { + ): Parser { + return flatMap(parser, (value) => (input, ..._) => { const result: Result = predicate(value) ? Ok.of([input, value] as const) : Err.of(ifError()); @@ -40,14 +45,14 @@ export namespace Parser { }); } - export function zeroOrMore( - parser: Parser - ): Parser, E> { - return (input) => { + export function zeroOrMore>( + parser: Parser + ): Parser, E, A> { + return (input, ...args) => { const values: Array = []; while (true) { - const result = parser(input); + const result = parser(input, ...args); if (result.isOk()) { const [remainder, value] = result.get(); @@ -63,23 +68,23 @@ export namespace Parser { }; } - export function oneOrMore( - parser: Parser - ): Parser, E> { + export function oneOrMore>( + parser: Parser + ): Parser, E, A> { return flatMap(parser, (head) => map(zeroOrMore(parser), (tail) => [head, ...tail]) ); } - export function take( - parser: Parser, + export function take>( + parser: Parser, n: number - ): Parser, E> { - return (input) => { + ): Parser, E, A> { + return (input, ...args) => { const values: Array = []; for (let i = 0; i < n; i++) { - const result = parser(input); + const result = parser(input, ...args); if (result.isOk()) { const [remainder, value] = result.get(); @@ -95,25 +100,28 @@ export namespace Parser { }; } - export function peek(parser: Parser): Parser { - return (input) => parser(input).map(([, value]) => [input, value]); + export function peek>( + parser: Parser + ): Parser { + return (input, ...args) => + parser(input, ...args).map(([, value]) => [input, value]); } - export function tee( - parser: Parser, + export function tee>( + parser: Parser, callback: Callback - ): Parser { + ): Parser { return map(parser, (value) => { callback(value); return value; }); } - export function option( - parser: Parser - ): Parser, E> { - return (input) => { - const result = map(parser, (value) => Option.of(value))(input); + export function option>( + parser: Parser + ): Parser, E, A> { + return (input, ...args) => { + const result = map(parser, (value) => Option.of(value))(input, ...args); if (result.isOk()) { return result; @@ -123,66 +131,66 @@ export namespace Parser { }; } - export function either( - left: Parser, - right: Parser - ): Parser { - return (input) => { - const result = left(input); + export function either>( + left: Parser, + right: Parser + ): Parser { + return (input, ...args) => { + const result = left(input, ...args); if (result.isOk()) { return result; } - return right(input); + return right(input, ...args); }; } - export function pair( - left: Parser, - right: Parser - ): Parser { + export function pair>( + left: Parser, + right: Parser + ): Parser { return flatMap(left, (left) => map(right, (right) => [left, right])); } - export function left( - left: Parser, - right: Parser - ): Parser { + export function left>( + left: Parser, + right: Parser + ): Parser { return flatMap(left, (left) => map(right, () => left)); } - export function right( - left: Parser, - right: Parser - ): Parser { + export function right>( + left: Parser, + right: Parser + ): Parser { return flatMap(left, () => map(right, (right) => right)); } - export function delimited( - left: Parser, - separator: Parser, - right: Parser = left - ): Parser { + export function delimited>( + left: Parser, + separator: Parser, + right: Parser = left + ): Parser { return flatMap(left, () => flatMap(separator, (separator) => map(right, () => separator)) ); } - export function separated( - left: Parser, - separator: Parser, - right: Parser - ): Parser { + export function separated>( + left: Parser, + separator: Parser, + right: Parser + ): Parser { return flatMap(left, (left) => flatMap(separator, () => map(right, (right) => [left, right])) ); } - export function separatedList( - parser: Parser, - separator: Parser - ): Parser, E> { + export function separatedList>( + parser: Parser, + separator: Parser + ): Parser, E, A> { return map( pair(parser, zeroOrMore(right(separator, parser))), ([first, rest]) => [first, ...rest]