diff --git a/types/parsimmon/index.d.ts b/types/parsimmon/index.d.ts index 96909be96a..d8ea7c3b17 100644 --- a/types/parsimmon/index.d.ts +++ b/types/parsimmon/index.d.ts @@ -1,12 +1,13 @@ -// Type definitions for Parsimmon 1.6 +// Type definitions for Parsimmon 1.10 // Project: https://github.com/jneen/parsimmon // Definitions by: Bart van der Schoor // Mizunashi Mana // Boris Cherny // Benny van Reeven // Leonard Thieu +// Jonathan Frere // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.2 +// TypeScript Version: 2.3 /** * **NOTE:** You probably will never need to use this function. Most parsing @@ -126,6 +127,14 @@ declare namespace Parsimmon { */ // tslint:disable-next-line:unified-signatures then(anotherParser: Parser): Parser; + /** + * Transforms the input of parser with the given function. + */ + contramap(fn: (input: T) => U): Parser; + /** + * Transforms the input and output of parser with the given function. + */ + promap(inputFn: (input: T) => U, outputFn: (output: U) => V): Parser; /** * returns wrapper(this) from the parser. Useful for custom functions used * to wrap your parsers, while keeping with Parsimmon chaining style. @@ -151,6 +160,10 @@ declare namespace Parsimmon { * expects otherParser after parser, but preserves the yield value of parser. */ skip(otherParser: Parser): Parser; + /** + * Expects the parser before before parser and after after parser. + */ + wrap(before: Parser, after: Parser): Parser; /** * Returns a parser that looks for anything but whatever anotherParser wants to * parse, and does not consume it. Yields the same result as parser. Equivalent to @@ -163,6 +176,17 @@ declare namespace Parsimmon { * parser.skip(Parsimmon.lookahead(anotherParser)). */ lookahead(arg: Parser | string | RegExp): Parser; + /** + * Equivalent to parser.tieWith(""). + * + * Note: parser.tie() is usually used after Parsimmon.seq(...parsers) or parser.many(). + */ + tie(): Parser; + /** + * When called on a parser yielding an array of strings, yields all their strings + * concatenated with the separator. Asserts that its input is actually an array of strings. + */ + tieWith(join: string): Parser; /** * expects parser zero or more times, and yields an array of the results. */ @@ -198,7 +222,34 @@ declare namespace Parsimmon { * Returns a new parser whose failure message is description. * For example, string('x').desc('the letter x') will indicate that 'the letter x' was expected. */ - desc(description: string): Parser; + desc(description: string | string[]): Parser; + + // Fantasy land support + + /** + * Returns Parsimmon.fail("fantasy-land/empty"). + */ + empty(): Parser; + /** + * Takes parser which returns a function and applies it to the parsed value of otherParser. + */ + ap(otherParser: Parser<(t: T) => U>): Parser; + /** + * Equivalent to Parsimmon.sepBy(parser, separator). + * + * Expects zero or more matches for parser, separated by the parser separator, yielding an array. + */ + sepBy(separator: Parser): Parser; + /** + * Equivalent to Parsimmon.sepBy(parser, separator). + * + * Expects one or more matches for parser, separated by the parser separator, yielding an array. + */ + sepBy1(separator: Parser): Parser; + /** + * Equivalent to Parsimmon.of(result). + */ + of(result: U): Parser; } /** @@ -265,7 +316,7 @@ declare namespace Parsimmon { * far the unsuccessful parse went (index), and what kind of syntax it * expected to see (expectation). See documentation for Parsimmon(fn). */ - function makeFailure(furthest: number, expectation: string): FailureReply; + function makeFailure(furthest: number, expectation: string | string[]): FailureReply; /** * Returns true if obj is a Parsimmon parser, otherwise false. @@ -287,6 +338,11 @@ declare namespace Parsimmon { */ function noneOf(string: string): Parser; + /** + * Parsers a single character in from begin to end, inclusive. + */ + function range(begin: string, end: string): Parser; + /** * Returns a parser that looks for a match to the regexp and yields the given match group * (defaulting to the entire match). The regexp will always match starting at the current @@ -358,6 +414,8 @@ declare namespace Parsimmon { p1: Parser, p2: Parser, p3: Parser, p4: Parser, p5: Parser, p6: Parser, p7: Parser, p8: Parser, cb: (a1: T, a2: U, a3: V, a4: W, a5: X, a6: Y, a7: Z, a8: A) => B): Parser; + function seqObj(...args: Array<[Key, Parser] | Parser>): Parser<{ [K in Key]: T[K] }>; + interface SuccessReply { status: true; index: number; @@ -413,6 +471,11 @@ declare namespace Parsimmon { */ function fail(message: string): Parser; + /** + * Returns Parsimmon.fail("fantasy-land/empty"). + */ + function empty(): Parser; + /** * is equivalent to Parsimmon.regex(/[a-z]/i) */ @@ -437,6 +500,41 @@ declare namespace Parsimmon { * is equivalent to Parsimmon.regex(/\s*`/) */ const optWhitespace: Parser; + /** + * Equivalent to Parsimmon.string("\r"). + * + * This parser checks for the "carriage return" character, which is used as the + * line terminator for classic Mac OS 9 text files. + */ + const cr: Parser; + /** + * Equivalent to Parsimmon.string("\n"). + * + * This parser checks for the "line feed" character, which is used as the line + * terminator for Linux and macOS text files. + */ + const lf: Parser; + /** + * Equivalent to Parsimmon.string("\r\n"). + * + * This parser checks for the "carriage return" character followed by the "line + * feed" character, which is used as the line terminator for Windows text files + * and HTTP headers. + */ + const crlf: Parser; + /** + * This flexible parser will match any kind of text file line ending. + */ + const newline: Parser; + /** + * Equivalent to Parsimmon.alt(Parsimmon.newline, Parsimmon.eof). + * + * This is the most general purpose "end of line" parser. It allows the "end of file" + * in addition to all three text file line endings from Parsimmon.newline. This is + * important because text files frequently do not have line terminators at the + * end ("trailing newline"). + */ + const end: Parser; /** * consumes and yields the next character of the stream. */ @@ -461,6 +559,23 @@ declare namespace Parsimmon { * Returns a parser yield a string containing all the next characters that pass the predicate */ function takeWhile(predicate: (char: string) => boolean): Parser; + /** + * Returns a parser that yields a byte (as a number) that matches the given input; + * similar to Parsimmon.digit and Parsimmon.letter. + */ + function byte(int: number): Parser; + /** + * Returns a parser that yields a byte (as a number) that matches the given input; + * similar to Parsimmon.digit and Parsimmon.letter. + */ + function bitSeq(alignments: number[]): Parser; + /** + * Works like Parsimmon.bitSeq except each item in the array is either a number of + * bits or pair (array with length = 2) of name and bits. The bits are parsed in order + * and put into an object based on the name supplied. If there's no name for the bits, + * it will be parsed but discarded from the returned value. + */ + function bitSeqObj(namedAlignments: Array<[Key, number] | number>): Parser<{ [K in Key]: number }>; } export = Parsimmon; diff --git a/types/parsimmon/parsimmon-tests.ts b/types/parsimmon/parsimmon-tests.ts index dbcf3d0753..0f2d296c5a 100644 --- a/types/parsimmon/parsimmon-tests.ts +++ b/types/parsimmon/parsimmon-tests.ts @@ -69,6 +69,7 @@ let fooReply: Reply; fooReply = P.makeSuccess(0, foo); fooReply = P.makeFailure(0, ''); +fooReply = P.makeFailure(0, ['', '']); fooPar = P((input: string, i: number) => P.makeSuccess(0, foo)); fooPar = P.Parser((input: string, i: number) => P.makeSuccess(0, foo)); @@ -97,10 +98,27 @@ barPar = fooPar.map((f) => { return bar; }); +strPar = P.string(str); + +strPar = strPar.contramap((f) => { + f; // $ExpectType string + return f.toUpperCase(); +}); + +barPar = strPar.promap((f) => { + f; // $ExpectType string + return 3; +}, (f) => { + f; // $ExpectType number + return bar; +}); + // -- -- -- -- -- -- -- -- -- -- -- -- -- fooPar = fooPar.skip(barPar); +fooPar = fooPar.wrap(barPar, strPar); + barPar = barPar = fooPar.result(bar); fooOrBarPar = fooPar.fallback(bar); @@ -116,6 +134,7 @@ fooArrPar = fooPar.atLeast(num); fooMarkPar = fooPar.mark(); fooPar = fooPar.desc(str); +fooPar = fooPar.desc([str, str]); // -- -- -- -- -- -- -- -- -- -- -- -- -- @@ -128,6 +147,19 @@ fooArrPar = P.seq(fooPar, fooPar); const par: Parser<[Bar, Foo, number]> = P.seq(barPar, fooPar, numPar); const par2: Parser = P.seq(barPar, fooPar, numPar).map(([a, b, c]: [Bar, Foo, number]) => 42); +interface SeqObj { + first: number; + second: string; + third: Foo; +} + +const seqObjPar: Parser = P.seqObj( + ['first', numPar], + barPar, + fooArrPar, + ['third', fooPar], + ['second', strPar]); + fooPar = P.custom((success, failure) => (stream, i) => { str = stream; num = i; return success(num, foo); }); fooPar = P.custom((success, failure) => (stream, i) => failure(num, str)); @@ -141,6 +173,36 @@ fooPar = P.lazy(() => { voidPar = P.fail(str); fooPar = P.fail(str); +fooPar = P.empty(); // $ExpectType Parser + +// -- -- -- -- -- -- -- -- -- -- -- -- -- + +const bytePar: Parser = P.byte(3); + +const byteParMany: Parser = P.bitSeq([1, 2, 5, 1]); + +interface ByteSeqObj { + first: number; + second: number; + third: number; +} + +const byteParObj: Parser = P.bitSeqObj([ + ['first', 3], + 6, + ['second', 8], + 7, + ['third', 9], +]); + +const byteParObjErr: Parser = P.bitSeqObj([ // $ExpectError + ['first', 3], + 6, + ['second', 8], + 7, + /* missing 'third' key */ +]); + // -- -- -- -- -- -- -- -- -- -- -- -- -- strPar = P.letter; @@ -152,6 +214,12 @@ strPar = P.digits; strPar = P.whitespace; strPar = P.optWhitespace; +strPar = P.cr; +strPar = P.lf; +strPar = P.crlf; +strPar = P.newline; +const voidOrStrPar: Parser = P.end; + strPar = P.any; strPar = P.all; voidPar = P.eof; @@ -166,6 +234,8 @@ bool = P.isParser(42); strPar = P.oneOf('a'); strPar = P.noneOf('a'); +strPar = P.range('a', 'z'); + strPar = P.regex(/foo/); strPar = P.regex(/foo/, 3); strPar = P.regexp(/bar/); @@ -176,6 +246,10 @@ emptyStrPar = P.lookahead(str); emptyStrPar = P.lookahead(/foo/); emptyStrPar = P.lookahead(fooPar); +strPar = strPar.tie(); + +strPar = strPar.tieWith(""); + fooPar = P.of(foo); str = P.formatError('foo', strPar.parse('bar')); @@ -214,6 +288,22 @@ function makeNode(name: Name) { let node: P.Parser> = P.letters.node('identifier'); node = P.letters.thru(makeNode('identifier')); +// -- -- -- -- -- -- -- -- -- -- -- -- -- +// Fantasy Land support + +fooPar = fooPar.empty(); // $ExpectType Parser + +// example taken from the documentation for the #ap method +numPar = P.digit + .ap(P.digit + .map(s => (t: string) => + Number(s) + Number(t))); + +fooArrPar = fooPar.sepBy(barPar); +fooArrPar = fooPar.sepBy1(barPar); + +fooPar = barPar.of(foo); + // -- -- -- -- -- -- -- -- -- -- -- -- -- let language: Language;