Parser Combinators¶
Parser combinators are a parsing technique from functional programming that let you build big parsers out of little parsers. Example of parsing combinator libraries include parsec in Haskell and parsimmon in JavaScript.
Parsing syntax in a macro without help is a bit of a chore since you must manage the state of the transformer context yourself; calls to mark and reset make following the parsing logic in the macro difficult. Parser combinators help by threading the context behind the scenes letting you focus on the logic of what you are trying to match.
For example, here is a macro written with parser combinators that matches a simplified class syntax:
import * as C from '@sweet-js/helpers/combinators' for syntax;
import { Either } from '@sweet-js/helpers/either' for syntax;
syntax class = ctx => {
let comma = C.punctuatorWith(v => v === ',');
let method = C.sequence(
C.identifier,
C.parensInner(C.sepBy(C.identifier, comma)),
C.braces
);
let result = C.sequence(
C.identifier,
C.bracesInner(C.many(method))
).run(ctx);
if (Either.isRight(result)) {
return #`'Parse success!'`;
}
return #`'Parse failed!'`;
}
class C {
f(a, b) {
return a + b;
}
}
Parser¶
Abstractly, a parser is just a function from the input stream to a value and the (potentially) modified input stream (or a failure). Concretely, the Parser() class provides the parser abstraction:
-
class
Parser(runner)¶ With type parameter
A, the type of the value that this parser produces.Import the
Parser()class via:import Parser from '@sweet-js/helpers/parser';
Arguments: - runner (
ImmutableContext=>Either<string, [A,ImmutableContext]>) – Gets called when :meth:`run` is invoked. Must return anEither()ofstringto represent failure or a tuple of the result value with typeAand the potentially modifiedImmutableContext().
For example, you can write a parser that consumes a single value like so:
import Parser from '@sweet-js/helpers/parser'; import { Maybe } from '@sweet-js/helpers/maybe'; import { Either, Left } from '@sweet-js/helpers/either'; let one = new Parser(ctx => { let i = ctx.head(); if (Maybe.isJust(i)) { return Either.of([i.value, ctx.rest()]); } else { return new Left('no more tokens to consume'); } });
The static methods of
Parser()are:The prototype methods of
Parser()are:- runner (
Combinators¶
-
sequence(...parsers)¶ Runs the provided parsers in sequence. The resulting parser only succeeds if all parsers succeed. The resulting parser contains an array of each of the sequenced parsers success. For example,
import * as C from '@sweet-js/helpers/combinators'; import Parser from '@sweet-js/helpers/parser'; C.sequence(Parser.of(1), Parser.of(2), Parser.of(3))
would result in a parser with
[1, 2, 3]as its result.Arguments: - parsers (...
Parser<A>) – The parsers to sequence.
Return type: Parser<A[]>- parsers (...
-
disj(a, b[, msg])¶ Returns a parser that attempts to run the
aparser and if it fails runs thebparser instead.Arguments: Return type: Parser<A>
-
many(p)¶ Returns a parser that runs parser
pon the context until it no longer succeeds, producing an array of each success value.Arguments: - p (
Parser<A>) – The parser to run
Return type: Parser<A[]>- p (
-
many1(p)¶ Like
many()but must match at least once.Arguments: - p (
Parser<A>) – The parser to run
Return type: Parser<A[]>- p (
-
sepBy(p, sep)¶ Like
many()but must match the separator parsersep.Arguments: Return type: Parser<A[]>
-
infixl(p, op)¶ Matches parser
pas a left associative infix operator, transforming each operand pair with the binary function inop. Similar tosepBy()but usesopto “reduce” the result.Arguments:
Syntax specific combinators¶
-
identifierWith(pred)¶ Returns a parser that matches a single identifier that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
keywordWith()¶ Returns a parser that matches a single keyword that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
punctuator()¶ Returns a parser that matches a single punctuator (e.g.
.,*,;, etc.).Return type: Parser<Term>
-
punctuatorWith()¶ Returns a parser that matches a single punctuator that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
numericWith()¶ Returns a parser that matches a single numeric that also matches the provided predicate.
Arguments: - pred (
number=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
stringWith()¶ Returns a parser that matches a single string that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
templateElement()¶ Returns a parser that matches a single template element.
Return type: Parser<Term>
-
templateElementWith()¶ Returns a parser that matches a single template element that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
templateWith()¶ Returns a parser that matches a single template that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
regexWith()¶ Returns a parser that matches a single regex that also matches the provided predicate.
Arguments: - pred (
string=>boolean) – A predicate to test the matched syntax
Return type: Parser<Term>- pred (
-
delimiterInner(p)¶ Returns a parser that matches any delimiter that also matches the provided parser on the syntax inside the delimiter. For example,
import * as C from '@sweet-js/helpers/combinators'; C.delimiterInner(C.many(C.identifier()))
would match syntax like
(),(foo bar).Arguments: - p (
Parser<A>) – The parser to run inside the delimiter.
Return type: Parser<A>- p (
-
bracesInner(p)¶ Returns a parser that matches any brace syntax that also matches the provided parser on the syntax inside the delimiter. For example,
import * as C from '@sweet-js/helpers/combinators'; C.bracesInner(C.many(C.identifier()))
would match syntax like
{},{foo bar}.Arguments: - p (
Parser<A>) – The parser to run inside the delimiter.
Return type: Parser<A>- p (
-
bracketsInner(p)¶ Returns a parser that matches any brackets syntax that also matches the provided parser on the syntax inside the delimiter. For example,
import * as C from '@sweet-js/helpers/combinators'; C.bracketsInner(C.many(C.identifier()))
would match syntax like
[],[foo bar].Arguments: - p (
Parser<A>) – The parser to run inside the delimiter.
Return type: Parser<A>- p (
-
parensInner(p)¶ Returns a parser that matches any parenthses syntax that also matches the provided parser on the syntax inside the delimiter. For example,
import * as C from '@sweet-js/helpers/combinators'; C.parensInner(C.many(C.identifier()))
would match syntax like
(),(foo bar).Arguments: - p (
Parser<A>) – The parser to run inside the delimiter.
Return type: Parser<A>- p (