Skip to content

Commit

Permalink
feat(parse): add lookahead() combinator, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Jul 5, 2020
1 parent 415fcbd commit ee35038
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
47 changes: 47 additions & 0 deletions packages/parse/src/combinators/lookahead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { Parser } from "../api";

/**
* Repeatedly runs look-`ahead` and `main` parsers until the former
* succeeds or end of input is reached.
*
* @remarks
* Result of `ahead` parser will NOT be cosumed and on successful match
* the final read position will be at beginning of `ahead` pattern. If
* the `ahead` parser never succeeds, the entire parser fails and any
* partial matches are discarded.
*
* @example
* ```ts
* const ctx = defContext("ababaaabbabba");
*
* // consume while 'a' or `b` until 1st occurrence of "abba"
* join(lookahead(oneOf("ab"), stringD("abba")))(ctx)
* // true
*
* ctx.result
* // 'ababaa'
*
* ctx.state
* // { p: 6, l: 1, c: 7, done: false, last: 'a' }
* ```
*
* @param parser
* @param ahead
* @param id
*/
export const lookahead = <T>(
parser: Parser<T>,
ahead: Parser<T>,
id = "lookahead"
): Parser<T> => (ctx) => {
if (ctx.done) return false;
ctx.start(id);
while (true) {
const state = { ...ctx.state };
if (ahead(ctx)) {
ctx.state = state;
return ctx.end();
}
if (!parser(ctx)) return ctx.discard();
}
};
1 change: 1 addition & 0 deletions packages/parse/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from "./combinators/boundary";
export * from "./combinators/check";
export * from "./combinators/dynamic";
export * from "./combinators/expect";
export * from "./combinators/lookahead";
export * from "./combinators/maybe";
export * from "./combinators/not";
export * from "./combinators/repeat";
Expand Down
32 changes: 32 additions & 0 deletions packages/parse/test/lookahead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as assert from "assert";
import { defContext, join, lookahead, oneOf, stringD, string } from "../src";

describe("lookahead", () => {
it("oneof", () => {
const ctx = defContext("ababaaabbabba");
assert(join(lookahead(oneOf("ab"), stringD("abba")))(ctx));
assert.equal(ctx.result, "ababaa");
assert.deepEqual(ctx.state, {
p: 6,
l: 1,
c: 7,
done: false,
last: "a",
});
assert(string("abba")(ctx));
});

it("string", () => {
const ctx = defContext("abababbabba");
assert(join(lookahead(string("ab"), stringD("abba")))(ctx));
assert.equal(ctx.result, "abab");
assert.deepEqual(ctx.state, {
p: 4,
l: 1,
c: 5,
done: false,
last: "b",
});
assert(string("abba")(ctx));
});
});

0 comments on commit ee35038

Please sign in to comment.