parser-ts
Version:
String parser combinators for TypeScript
699 lines (698 loc) • 23.3 kB
JavaScript
;
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;