Skip to content

Commit

Permalink
Made manySepBy push the separators into a key.
Browse files Browse the repository at this point in the history
  • Loading branch information
GregRos committed Sep 6, 2023
1 parent 51debff commit 657f9b3
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 43 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "parjs",
"version": "0.15.2",
"version": "0.16.0",
"repository": "https://github.com/GregRos/parjs",
"homepage": "https://github.com/GregRos/parjs",
"exports": {
Expand Down
1 change: 1 addition & 0 deletions src/lib/combinators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
map,
manyTill,
manySepBy,
ArrayWithSeparators,
reason,
many,
later,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/internal/combinators/combinator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ScalarConverter } from "../scalar-converter";
* Represents the given function as a Parjs combinator.
* @param f The combinator function.
*/
export function defineCombinator(f: (act: ParjserBase) => Parjser<any>) {
export function defineCombinator<E>(f: (act: ParjserBase & Parjser<E>) => Parjser<any>) {
return (x: ImplicitParjser<any>) => {
const resolved = ScalarConverter.convert(x);
return f(resolved as ParjserBase);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/internal/combinators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export { backtrack } from "./backtrack";
export { exactly } from "./exactly";
export { later, DelayedParjser } from "./later";
export { many } from "./many";
export { manySepBy } from "./many-sep-by";
export { manySepBy, ArrayWithSeparators } from "./many-sep-by";
export { manyTill, manyBetween } from "./many-till";
export { map, mapConst } from "./map";
export { must } from "./must";
Expand Down
36 changes: 23 additions & 13 deletions src/lib/internal/combinators/many-sep-by.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@
import { Issues } from "../issues";
import { ResultKind } from "../result";
import { ParsingState } from "../state";
import { ImplicitParjser, ParjsCombinator } from "../../index";
import { ImplicitParjser, ParjsCombinator, Parjser } from "../../index";
import { ScalarConverter } from "../scalar-converter";
import { ParjserBase } from "../parser";
import { defineCombinator } from "./combinator";

export type ArrayWithSeparators<Normal, Separator> = Normal[] & {
separators: Separator[];
};

export function getArrayWithSeparators<T, S>(arr: T[], separators: S[]): ArrayWithSeparators<T, S> {
(arr as any).separators = separators;
return arr as any;
}

/**
* Applies the source parser repeatedly until it fails softly, with each pair of
* applications separated by applying `delimeter`. Also terminates if `delimeter`
Expand All @@ -19,30 +28,31 @@ import { defineCombinator } from "./combinator";
* @param max Optionally, then maximum number of times to apply the source
* parser. Defaults to `Infinity`.
*/
export function manySepBy<T>(
delimeter: ImplicitParjser<any>,
export function manySepBy<E, S>(
delimeter: ImplicitParjser<S>,
max?: number
): ParjsCombinator<T, T[]>;
): ParjsCombinator<E, ArrayWithSeparators<E, S>>;

export function manySepBy(implDelimeter: ImplicitParjser<any>, max = Infinity) {
const delimeter = ScalarConverter.convert(implDelimeter) as any as ParjserBase;
return defineCombinator(source => {
export function manySepBy<E, S>(implDelimeter: ImplicitParjser<S>, max = Infinity) {
const delimeter = ScalarConverter.convert(implDelimeter) as ParjserBase & Parjser<S>;
return defineCombinator<E>(source => {
return new (class extends ParjserBase {
type = "manySepBy";
expecting = source.expecting;

_apply(ps: ParsingState): void {
const arr = [] as any[];
const results = getArrayWithSeparators<E, S>([], []);

source.apply(ps);
if (ps.atLeast(ResultKind.HardFail)) {
return;
} else if (ps.isSoft) {
ps.value = [];
ps.value = results;
ps.kind = ResultKind.Ok;
return;
}
let { position } = ps;
arr.push(ps.value);
results.push(ps.value);
let i = 1;
for (;;) {
if (i >= max) break;
Expand All @@ -52,7 +62,7 @@ export function manySepBy(implDelimeter: ImplicitParjser<any>, max = Infinity) {
} else if (ps.atLeast(ResultKind.HardFail)) {
return;
}

results.separators.push(ps.value);
source.apply(ps);
if (ps.isSoft) {
break;
Expand All @@ -62,13 +72,13 @@ export function manySepBy(implDelimeter: ImplicitParjser<any>, max = Infinity) {
if (max >= Infinity && ps.position === position) {
Issues.guardAgainstInfiniteLoop("many");
}
arr.push(ps.value);
results.push(ps.value);
position = ps.position;
i++;
}
ps.kind = ResultKind.Ok;
ps.position = position;
ps.value = arr;
ps.value = results;
}
})();
});
Expand Down
23 changes: 0 additions & 23 deletions src/publish.ts

This file was deleted.

12 changes: 8 additions & 4 deletions src/test/unit/combinators/sequential.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
thenPick,
manyBetween
} from "../../../lib/combinators";
import { getArrayWithSeparators } from "../../../lib/internal/combinators/many-sep-by";

const goodInput = "abcd";
const softBadInput = "a";
Expand Down Expand Up @@ -198,7 +199,7 @@ describe("sequential combinators", () => {
});

it("succeeds with empty input", () => {
expectSuccess(parser.parse(""), []);
expectSuccess(parser.parse(""), getArrayWithSeparators([], []));
});

it("many fails hard on 1st application", () => {
Expand All @@ -218,7 +219,7 @@ describe("sequential combinators", () => {

it("sep+many that don't consume succeed with max iterations", () => {
const parser2 = string("").pipe(manySepBy("", 2));
expectSuccess(parser2.parse(""), ["", ""]);
expectSuccess(parser2.parse(""), getArrayWithSeparators(["", ""], [""]));
});

it("many that fails hard on 2nd iteration", () => {
Expand All @@ -227,12 +228,15 @@ describe("sequential combinators", () => {
});

it("succeeds with non-empty input", () => {
expectSuccess(parser.parse("ab, ab"), ["ab", "ab"]);
expectSuccess(parser.parse("ab, ab"), getArrayWithSeparators(["ab", "ab"], [", "]));
});

it("chains into terminating separator", () => {
const parser2 = parser.pipe(thenq(", "));
expectSuccess(parser2.parse("ab, ab, "), ["ab", "ab"]);
expectSuccess(
parser2.parse("ab, ab, "),
getArrayWithSeparators(["ab", "ab"], [", ", ", "])
);
});
it("fails soft if first many fails", () => {
expectFailure(parser.parse("xa"), ResultKind.SoftFail);
Expand Down

0 comments on commit 657f9b3

Please sign in to comment.