UNPKG

parser-ts

Version:

String parser combinators for TypeScript

699 lines (698 loc) 23.3 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.bind = exports.bindTo = exports.parser = exports.Alternative = exports.Alt = exports.ChainRec = exports.Monad = exports.Applicative = exports.Functor = exports.getMonoid = exports.getSemigroup = exports.URI = exports.zero = exports.flatten = exports.alt = exports.chainFirst = exports.chain = exports.of = exports.apSecond = exports.apFirst = exports.ap = exports.map = exports.many1Till = exports.manyTill = exports.optional = exports.takeUntil = exports.lookAhead = exports.surroundedBy = exports.between = exports.filter = exports.sepByCut = exports.sepBy1 = exports.sepBy = exports.many1 = exports.many = exports.eof = exports.maybe = exports.withStart = exports.either = exports.seq = exports.cutWith = exports.cut = exports.item = exports.expected = exports.sat = exports.failAt = exports.fail = exports.succeed = void 0; var A = __importStar(require("fp-ts/lib/Array")); var ChainRec_1 = require("fp-ts/lib/ChainRec"); var E = __importStar(require("fp-ts/lib/Either")); var function_1 = require("fp-ts/lib/function"); var NEA = __importStar(require("fp-ts/lib/NonEmptyArray")); var O = __importStar(require("fp-ts/lib/Option")); var Predicate_1 = require("fp-ts/lib/Predicate"); var RA = __importStar(require("fp-ts/lib/ReadonlyArray")); var RNEA = __importStar(require("fp-ts/lib/ReadonlyNonEmptyArray")); var ParseResult_1 = require("./ParseResult"); var Stream_1 = require("./Stream"); // ------------------------------------------------------------------------------------- // constructors // ------------------------------------------------------------------------------------- /** * The `succeed` parser constructor creates a parser which will simply * return the value provided as its argument, without consuming any input. * * This is equivalent to the monadic `of`. * * @category constructors * @since 0.6.0 */ var succeed = function (a) { return function (i) { return (0, ParseResult_1.success)(a, i, i); }; }; exports.succeed = succeed; /** * The `fail` parser will just fail immediately without consuming any input * * @category constructors * @since 0.6.0 */ var fail = function () { return function (i) { return (0, ParseResult_1.error)(i); }; }; exports.fail = fail; /** * The `failAt` parser will fail immediately without consuming any input, * but will report the failure at the provided input position. * * @category constructors * @since 0.6.0 */ var failAt = function (i) { return function () { return (0, ParseResult_1.error)(i); }; }; exports.failAt = failAt; /** * The `sat` parser constructor takes a predicate function, and will consume * a single character if calling that predicate function with the character * as its argument returns `true`. If it returns `false`, the parser will * fail. * * @category constructors * @since 0.6.0 */ var sat = function (predicate) { return (0, function_1.pipe)((0, exports.withStart)((0, exports.item)()), (0, exports.chain)(function (_a) { var a = _a[0], start = _a[1]; return (predicate(a) ? (0, exports.of)(a) : (0, exports.failAt)(start)); })); }; exports.sat = sat; // ------------------------------------------------------------------------------------- // combinators // ------------------------------------------------------------------------------------- /** * A parser combinator which returns the provided parser unchanged, except * that if it fails, the provided error message will be returned in the * ParseError`. * * @category combinators * @since 0.6.0 */ var expected = function (p, message) { return function (i) { return (0, function_1.pipe)(p(i), E.mapLeft(function (err) { return (0, ParseResult_1.withExpected)(err, [message]); })); }; }; exports.expected = expected; /** * The `item` parser consumes a single value, regardless of what it is, * and returns it as its result. * * @category combinators * @since 0.6.0 */ var item = function () { return function (i) { return (0, function_1.pipe)((0, Stream_1.getAndNext)(i), O.fold(function () { return (0, ParseResult_1.error)(i); }, function (_a) { var value = _a.value, next = _a.next; return (0, ParseResult_1.success)(value, next, i); })); }; }; exports.item = item; /** * The `cut` parser combinator takes a parser and produces a new parser for * which all errors are fatal, causing either to stop trying further * parsers and return immediately with a fatal error. * * @category combinators * @since 0.6.0 */ var cut = function (p) { return function (i) { return (0, function_1.pipe)(p(i), E.mapLeft(ParseResult_1.escalate)); }; }; exports.cut = cut; /** * Takes two parsers `p1` and `p2`, returning a parser which will match * `p1` first, discard the result, then either match `p2` or produce a fatal * error. * * @category combinators * @since 0.6.0 */ var cutWith = function (p1, p2) { return (0, function_1.pipe)(p1, (0, exports.apSecond)((0, exports.cut)(p2))); }; exports.cutWith = cutWith; /** * The `seq` combinator takes a parser, and a function which will receive * the result of that parser if it succeeds, and which should return another * parser, which will be run immediately after the initial parser. In this * way, you can join parsers together in a sequence, producing more complex * parsers. * * This is equivalent to the monadic `chain` operation. * * @category combinators * @since 0.6.0 */ var seq = function (fa, f) { return function (i) { return (0, function_1.pipe)(fa(i), E.chain(function (s) { return (0, function_1.pipe)(f(s.value)(s.next), E.chain(function (next) { return (0, ParseResult_1.success)(next.value, next.next, i); })); })); }; }; exports.seq = seq; /** * The `either` combinator takes two parsers, runs the first on the input * stream, and if that fails, it will backtrack and attempt the second * parser on the same input. Basically, try parser 1, then try parser 2. * * If the first parser fails with an error flagged as fatal (see `cut`), * the second parser will not be attempted. * * This is equivalent to the `alt` operation. * * @category combinators * @since 0.6.0 */ var either = function (p, f) { return function (i) { var e = p(i); if (E.isRight(e)) { return e; } if (e.left.fatal) { return e; } return (0, function_1.pipe)(f()(i), E.mapLeft(function (err) { return (0, ParseResult_1.extend)(e.left, err); })); }; }; exports.either = either; /** * Converts a parser into one which will return the point in the stream where * it started parsing in addition to its parsed value. * * Useful if you want to keep track of where in the input stream a parsed * token came from. * * @category combinators * @since 0.6.0 */ var withStart = function (p) { return function (i) { return (0, function_1.pipe)(p(i), E.map(function (s) { return (__assign(__assign({}, s), { value: [s.value, i] })); })); }; }; exports.withStart = withStart; /** * The `maybe` parser combinator creates a parser which will run the provided * parser on the input, and if it fails, it will returns the empty value (as * defined by `empty`) as a result, without consuming any input. * * @category combinators * @since 0.6.0 */ var maybe = function (M) { return (0, exports.alt)(function () { return (0, exports.of)(M.empty); }); }; exports.maybe = maybe; /** * Matches the end of the stream. * * @category combinators * @since 0.6.0 */ var eof = function () { return (0, exports.expected)(function (i) { return ((0, Stream_1.atEnd)(i) ? (0, ParseResult_1.success)(undefined, i, i) : (0, ParseResult_1.error)(i)); }, 'end of file'); }; exports.eof = eof; /** * The `many` combinator takes a parser, and returns a new parser which will * run the parser repeatedly on the input stream until it fails, returning * a list of the result values of each parse operation as its result, or the * empty list if the parser never succeeded. * * Read that as "match this parser zero or more times and give me a list of * the results." * * @category combinators * @since 0.6.0 */ var many = function (p) { return (0, function_1.pipe)((0, exports.many1)(p), (0, exports.alt)(function () { return (0, exports.of)([]); })); }; exports.many = many; /** * The `many1` combinator is just like the `many` combinator, except it * requires its wrapped parser to match at least once. The resulting list is * thus guaranteed to contain at least one value. * * @category combinators * @since 0.6.0 */ var many1 = function (parser) { return (0, function_1.pipe)(parser, (0, exports.chain)(function (head) { return chainRec_(NEA.of(head), function (acc) { return (0, function_1.pipe)(parser, (0, exports.map)(function (a) { return E.left(A.append(a)(acc)); }), (0, exports.alt)(function () { return (0, exports.of)(E.right(acc)); })); }); })); }; exports.many1 = many1; /** * Matches the provided parser `p` zero or more times, but requires the * parser `sep` to match once in between each match of `p`. In other words, * use `sep` to match separator characters in between matches of `p`. * * @category combinators * @since 0.6.0 */ var sepBy = function (sep, p) { var nil = (0, exports.of)([]); return (0, function_1.pipe)((0, exports.sepBy1)(sep, p), (0, exports.alt)(function () { return nil; })); }; exports.sepBy = sepBy; /** * Matches the provided parser `p` one or more times, but requires the * parser `sep` to match once in between each match of `p`. In other words, * use `sep` to match separator characters in between matches of `p`. * * @category combinators * @since 0.6.0 */ var sepBy1 = function (sep, p) { return (0, function_1.pipe)(p, (0, exports.chain)(function (head) { return (0, function_1.pipe)((0, exports.many)((0, function_1.pipe)(sep, (0, exports.apSecond)(p))), (0, exports.map)(function (tail) { return A.prepend(head)(tail); })); })); }; exports.sepBy1 = sepBy1; /** * Like `sepBy1`, but cut on the separator, so that matching a `sep` not * followed by a `p` will cause a fatal error. * * @category combinators * @since 0.6.0 */ var sepByCut = function (sep, p) { return (0, function_1.pipe)(p, (0, exports.chain)(function (head) { return (0, function_1.pipe)((0, exports.many)((0, exports.cutWith)(sep, p)), (0, exports.map)(function (tail) { return A.prepend(head)(tail); })); })); }; exports.sepByCut = sepByCut; /** * Filters the result of a parser based upon a `Refinement` or a `Predicate`. * * @example * import { pipe } from 'fp-ts/function' * import { run } from 'parser-ts/code-frame' * import * as C from 'parser-ts/char' * import * as P from 'parser-ts/Parser' * * const parser = P.expected( * pipe( * P.item<C.Char>(), * P.filter((c) => c !== 'a') * ), * 'anything except "a"' * ) * * run(parser, 'a') * // { _tag: 'Left', left: '> 1 | a\n | ^ Expected: anything except "a"' } * * run(parser, 'b') * // { _tag: 'Right', right: 'b' } * * @category combinators * @since 0.6.10 */ var filter = function (predicate) { return function (p) { return function (i) { return (0, function_1.pipe)(p(i), E.chain(function (next) { return (predicate(next.value) ? E.right(next) : (0, ParseResult_1.error)(i)); })); }; }; }; exports.filter = filter; /** * Matches the provided parser `p` that occurs between the provided `left` and `right` parsers. * * `p` is polymorphic in its return type, because in general bounds and actual parser could return different types. * * @category combinators * @since 0.6.4 */ var between = function (left, right) { return function (p) { return (0, function_1.pipe)(left, (0, exports.chain)(function () { return p; }), (0, exports.chainFirst)(function () { return right; })); }; }; exports.between = between; /** * Matches the provided parser `p` that is surrounded by the `bound` parser. Shortcut for `between(bound, bound)`. * * @category combinators * @since 0.6.4 */ var surroundedBy = function (bound) { return (0, exports.between)(bound, bound); }; exports.surroundedBy = surroundedBy; /** * Takes a `Parser` and tries to match it without consuming any input. * * @example * import { run } from 'parser-ts/code-frame' * import * as P from 'parser-ts/Parser' * import * as S from 'parser-ts/string' * * const parser = S.fold([ * S.string('hello '), * P.lookAhead(S.string('world')), * S.string('wor') * ]) * * run(parser, 'hello world') * // { _tag: 'Right', right: 'hello worldwor' } * * @category combinators * @since 0.6.6 */ var lookAhead = function (p) { return function (i) { return (0, function_1.pipe)(p(i), E.chain(function (next) { return (0, ParseResult_1.success)(next.value, i, i); })); }; }; exports.lookAhead = lookAhead; /** * Takes a `Predicate` and continues parsing until the given `Predicate` is satisfied. * * @example * import * as C from 'parser-ts/char' * import { run } from 'parser-ts/code-frame' * import * as P from 'parser-ts/Parser' * * const parser = P.takeUntil((c: C.Char) => c === 'w') * * run(parser, 'hello world') * // { _tag: 'Right', right: [ 'h', 'e', 'l', 'l', 'o', ' ' ] } * * @category combinators * @since 0.6.6 */ var takeUntil = function (predicate) { return (0, exports.many)((0, exports.sat)((0, Predicate_1.not)(predicate))); }; exports.takeUntil = takeUntil; /** * Returns `Some<A>` if the specified parser succeeds, otherwise returns `None`. * * @example * import * as C from 'parser-ts/char' * import { run } from 'parser-ts/code-frame' * import * as P from 'parser-ts/Parser' * * const a = P.sat((c: C.Char) => c === 'a') * const parser = P.optional(a) * * run(parser, 'a') * // { _tag: 'Right', right: { _tag: 'Some', value: 'a' } } * * run(parser, 'b') * // { _tag: 'Left', left: { _tag: 'None' } } * * @category combinators * @since 0.6.10 */ var optional = function (parser) { return (0, function_1.pipe)(parser, (0, exports.map)(O.some), (0, exports.alt)(function () { return (0, exports.succeed)(O.none); })); }; exports.optional = optional; /** * The `manyTill` combinator takes a value `parser` and a `terminator` parser, and * returns a new parser that will run the value `parser` repeatedly on the input * stream, returning a list of the result values of each parse operation as its * result, or the empty list if the parser never succeeded. * * @example * import * as C from 'parser-ts/char' * import { run } from 'parser-ts/code-frame' * import * as P from 'parser-ts/Parser' * * const parser = P.manyTill(C.letter, C.char('-')) * * run(parser, 'abc-') * // { _tag: 'Right', right: [ 'a', 'b', 'c' ] } * * run(parser, '-') * // { _tag: 'Right', right: [] } * * @category combinators * @since 0.6.11 */ var manyTill = function (parser, terminator) { return (0, function_1.pipe)(terminator, (0, exports.map)(function () { return RA.empty; }), (0, exports.alt)(function () { return (0, exports.many1Till)(parser, terminator); })); }; exports.manyTill = manyTill; /** * The `many1Till` combinator is just like the `manyTill` combinator, except it * requires the value `parser` to match at least once before the `terminator` * parser. The resulting list is thus guaranteed to contain at least one value. * * @example * import * as C from 'parser-ts/char' * import { run } from 'parser-ts/code-frame' * import * as P from 'parser-ts/Parser' * * const parser = P.many1Till(C.letter, C.char('-')) * * run(parser, 'abc-') * // { _tag: 'Right', right: [ 'a', 'b', 'c' ] } * * run(parser, '-') * // { _tag: 'Left', left: '> 1 | -\n | ^ Expected: a letter' } * * @category combinators * @since 0.6.11 */ var many1Till = function (parser, terminator) { return (0, function_1.pipe)(parser, (0, exports.chain)(function (x) { return chainRec_(RNEA.of(x), function (acc) { return (0, function_1.pipe)(terminator, (0, exports.map)(function () { return E.right(acc); }), (0, exports.alt)(function () { return (0, function_1.pipe)(parser, (0, exports.map)(function (a) { return E.left(RA.append(a)(acc)); })); })); }); })); }; exports.many1Till = many1Till; var map_ = function (ma, f) { return function (i) { return (0, function_1.pipe)(ma(i), E.map(function (s) { return (__assign(__assign({}, s), { value: f(s.value) })); })); }; }; var ap_ = function (mab, ma) { return chain_(mab, function (f) { return map_(ma, f); }); }; var chain_ = function (ma, f) { return (0, exports.seq)(ma, f); }; var chainRec_ = function (a, f) { var split = function (start) { return function (result) { return E.isLeft(result.value) ? E.left({ value: result.value.left, stream: result.next }) : E.right((0, ParseResult_1.success)(result.value.right, result.next, start)); }; }; return function (start) { return (0, ChainRec_1.tailRec)({ value: a, stream: start }, function (state) { var result = f(state.value)(state.stream); if (E.isLeft(result)) { return E.right((0, ParseResult_1.error)(state.stream, result.left.expected, result.left.fatal)); } return split(start)(result.right); }); }; }; var alt_ = function (fa, that) { return (0, exports.either)(fa, that); }; // ------------------------------------------------------------------------------------- // pipeables // ------------------------------------------------------------------------------------- /** * @category Functor * @since 0.6.7 */ var map = function (f) { return function (fa) { return map_(fa, f); }; }; exports.map = map; /** * @category Apply * @since 0.6.7 */ var ap = function (fa) { return function (fab) { return ap_(fab, fa); }; }; exports.ap = ap; /** * @category Apply * @since 0.6.7 */ var apFirst = function (fb) { return function (fa) { return ap_(map_(fa, function (a) { return function () { return a; }; }), fb); }; }; exports.apFirst = apFirst; /** * @category Apply * @since 0.6.7 */ var apSecond = function (fb) { return function (fa) { return ap_(map_(fa, function () { return function (b) { return b; }; }), fb); }; }; exports.apSecond = apSecond; /** * @category Applicative * @since 0.6.7 */ exports.of = exports.succeed; /** * @category Monad * @since 0.6.7 */ var chain = function (f) { return function (ma) { return chain_(ma, f); }; }; exports.chain = chain; /** * @category Monad * @since 0.6.7 */ var chainFirst = function (f) { return function (ma) { return chain_(ma, function (a) { return map_(f(a), function () { return a; }); }); }; }; exports.chainFirst = chainFirst; /** * @category Alt * @since 0.6.7 */ var alt = function (that) { return function (fa) { return alt_(fa, that); }; }; exports.alt = alt; /** * @category Monad * @since 0.6.7 */ var flatten = function (mma) { return chain_(mma, function_1.identity); }; exports.flatten = flatten; /** * @category Alternative * @since 0.6.7 */ exports.zero = exports.fail; // ------------------------------------------------------------------------------------- // instances // ------------------------------------------------------------------------------------- /** * @category instances * @since 0.6.0 */ exports.URI = 'Parser'; /** * @category instances * @since 0.6.7 */ var getSemigroup = function (S) { return ({ concat: function (x, y) { return ap_(map_(x, function (x) { return function (y) { return S.concat(x, y); }; }), y); } }); }; exports.getSemigroup = getSemigroup; /** * @category instances * @since 0.6.0 */ var getMonoid = function (M) { return (__assign(__assign({}, (0, exports.getSemigroup)(M)), { empty: (0, exports.succeed)(M.empty) })); }; exports.getMonoid = getMonoid; /** * @category instances * @since 0.6.7 */ exports.Functor = { URI: exports.URI, map: map_ }; /** * @category instances * @since 0.6.7 */ exports.Applicative = { URI: exports.URI, map: map_, ap: ap_, of: exports.of }; /** * @category instances * @since 0.6.7 */ exports.Monad = { URI: exports.URI, map: map_, ap: ap_, of: exports.of, chain: chain_ }; /** * @category instances * @since 0.6.11 */ exports.ChainRec = { URI: exports.URI, map: map_, ap: ap_, chain: chain_, chainRec: chainRec_ }; /** * @category instances * @since 0.6.7 */ exports.Alt = { URI: exports.URI, map: map_, alt: alt_ }; /** * @category instances * @since 0.6.7 */ exports.Alternative = { URI: exports.URI, map: map_, of: exports.of, ap: ap_, alt: alt_, zero: exports.fail }; /** * @category instances * @since 0.6.7 */ exports.parser = { URI: exports.URI, map: map_, of: exports.of, ap: ap_, chain: chain_, alt: alt_, zero: exports.fail }; // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- /** * @internal */ var bind_ = function (a, name, b) { var _a; return Object.assign({}, a, (_a = {}, _a[name] = b, _a)); }; /** * @since 0.6.8 */ var bindTo = function (name) { return function (fa) { return (0, function_1.pipe)(fa, (0, exports.map)(function (a) { return bind_({}, name, a); })); }; }; exports.bindTo = bindTo; /** * @since 0.6.8 */ var bind = function (name, f) { return function (fa) { return (0, function_1.pipe)(fa, (0, exports.chain)(function (a) { return (0, function_1.pipe)(f(a), (0, exports.map)(function (b) { return bind_(a, name, b); })); })); }; }; exports.bind = bind;