Skip to content

Commit

Permalink
feat(parse): update repeat ops, reader, initial state
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Apr 15, 2020
1 parent a913c96 commit c5cfabe
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 19 deletions.
4 changes: 2 additions & 2 deletions packages/parse/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export interface ParseState<T> {
export interface IReader<T> {
read(state: ParseState<T>): T;
next(state: ParseState<T>): void;
set(state: ParseState<T>, pos: number): void;
info(state: ParseState<T>): string;
isDone(state: ParseState<T>): boolean;
format(state: ParseState<T>): string;
}

export type Parser<T> = Fn<ParseContext<T>, boolean>;
Expand Down
17 changes: 10 additions & 7 deletions packages/parse/src/combinators/repeat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { Parser } from "../api";

export const repeat = <T>(
parser: Parser<T>,
id = "repeat",
min: number,
max = Infinity
max: number,
id = "repeat"
): Parser<T> => (ctx) => {
if (ctx.done) return false;
if (ctx.done) return min < 1;
ctx.start(id);
for (let i = 0; i < max; i++) {
if (!parser(ctx)) {
Expand All @@ -22,8 +22,11 @@ export const repeat = <T>(
export const zeroOrMore = <T>(
parser: Parser<T>,
id = "repeat0",
max?: number
) => repeat(parser, id, 0, max);
max = Infinity
) => repeat(parser, 0, max, id);

export const oneOrMore = <T>(parser: Parser<T>, id = "repeat1", max?: number) =>
repeat(parser, id, 1, max);
export const oneOrMore = <T>(
parser: Parser<T>,
id = "repeat1",
max = Infinity
) => repeat(parser, 1, max, id);
4 changes: 2 additions & 2 deletions packages/parse/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ export const ALPHA = alt([range<string>("a", "z"), range<string>("A", "Z")]);

export const ALPHA_NUM = alt([ALPHA, DIGIT]);

export const SIGN = maybe(oneOf("-+"), "");

export const DIGITS_0 = zeroOrMore(DIGIT);

export const DIGITS_1 = oneOrMore(DIGIT);

export const HEX_DIGITS_1 = oneOrMore(HEX_DIGIT);

const SIGN = maybe(oneOf("-+"));

const EXP = maybe(seq([maybe(oneOf("eE")), SIGN, DIGITS_1]));

const DOT = lit(".");
Expand Down
1 change: 1 addition & 0 deletions packages/parse/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class ParseContext<T> {
result: null,
};
this._scopes = [this._curr];
reader.isDone(this._curr.state);
}

start(type: string) {
Expand Down
2 changes: 1 addition & 1 deletion packages/parse/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { ParseContext } from "./context";
const ParseError = defError(() => `ParseError`);

export const parseError = (ctx: ParseContext<any>, msg: string): never => {
const info = ctx.reader.info(ctx.scope.state);
const info = ctx.reader.format(ctx.scope.state);
throw new ParseError(msg + (info ? ` @ ${info}` : ""));
};
9 changes: 4 additions & 5 deletions packages/parse/src/string-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ParseState, IReader } from "./api";

class StringReader implements IReader<string> {
constructor(protected input: string) {}

read(state: ParseState<string>): string {
return this.input[state.p];
}
Expand All @@ -18,13 +19,11 @@ class StringReader implements IReader<string> {
state.done = ++state.p >= this.input.length;
}

set(state: ParseState<string>, pos: number): void {
if (state.done) return;
state.p = pos;
state.done = pos >= this.input.length;
isDone(state: ParseState<string>) {
return (state.done = state.p >= this.input.length);
}

info(state: ParseState<string>) {
format(state: ParseState<string>) {
return `offset ${state.p} (${state.l}:${state.c})`;
}
}
Expand Down
90 changes: 88 additions & 2 deletions packages/parse/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,101 @@ import * as assert from "assert";
import {
alt,
defContext,
DIGIT,
FLOAT,
INT,
oneOf,
oneOrMore,
Parser,
seq,
WS,
xform,
zeroOrMore,
} from "../src";

const check = (
parser: Parser<string>,
src: string,
res: boolean,
pos: number
) => {
const ctx = defContext(src);
assert.equal(parser(ctx), res, `src: '${src}'`);
assert.equal(ctx.state.p, pos, `src: '${src}' pos: ${ctx.state.p}`);
};

describe("parse", () => {
it("initial ctx", () => {
assert.deepEqual(defContext("").state, {
p: 0,
l: 1,
c: 1,
done: true,
});
assert.deepEqual(defContext(" ").state, {
p: 0,
l: 1,
c: 1,
done: false,
});
});

it("zeroOrMore", () => {
const ws = zeroOrMore(WS);
const p1 = seq([DIGIT, ws, DIGIT]);
const p2 = zeroOrMore(p1);

check(ws, "", true, 0);
check(ws, " ", true, 1);
check(ws, " ", true, 2);

check(p1, "", false, 0);
check(p1, "11", true, 2);
check(p1, "1 1", true, 3);
check(p1, "1 ", false, 0);
check(p1, "1 ", false, 0);
check(p1, "1 x", false, 0);

check(p2, "", true, 0);
check(p2, "11", true, 2);
check(p2, "1 1", true, 3);
check(p2, "1 x", true, 0);
check(p2, "1 122", true, 5);
});

it("oneOrMore", () => {
const ws = oneOrMore(WS);
const p1 = seq([DIGIT, ws, DIGIT]);
const p2 = oneOrMore(p1);
const p3 = oneOrMore(seq([DIGIT, zeroOrMore(WS), DIGIT]));

check(ws, "", false, 0);
check(ws, " ", true, 1);
check(ws, " ", true, 2);

check(p1, "", false, 0);
check(p1, "11", false, 0);
check(p1, "1 1", true, 3);
check(p1, "1 ", false, 0);
check(p1, "1 ", false, 0);
check(p1, "1 x", false, 0);

check(p2, "", false, 0);
check(p2, "11", false, 0);
check(p2, "1 1", true, 3);
check(p2, "1 x", false, 0);
check(p2, "1 12 2", true, 6);

check(p3, "", false, 0);
check(p3, "1", false, 0);
check(p3, "1x", false, 0);
check(p3, "1 x", false, 0);
check(p3, "11", true, 2);
check(p3, "1111", true, 4);
check(p3, "111 1", true, 5);
check(p3, "11x", true, 2);
});

it("float", () => {
[
"1",
Expand Down Expand Up @@ -43,13 +129,13 @@ describe("parse", () => {
stack.push(scope!.result);
return null;
});
const op = xform(oneOf("+-*/"), (scope) => {
const op = xform(oneOf(Object.keys(ops)), (scope) => {
const b = stack.pop()!;
const a = stack.pop()!;
stack.push(ops[scope!.result](a, b));
return null;
});
const program = zeroOrMore(alt([value, op, WS]));
const program = zeroOrMore(alt([value, op, zeroOrMore(WS)]));
program(defContext("10 5 3 * + -2 * 10 /"));
assert.deepEqual(stack, [-5]);
});
Expand Down
1 change: 1 addition & 0 deletions packages/parse/tpl.readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Source: [/combinators](https://github.com/thi-ng/umbrella/tree/feature/parse/pac
- `alt` -
- `maybe` -
- `not` -
- `oneOrMore` / `zeroOrMore` -
- `repeat` -
- `seq` -

Expand Down

0 comments on commit c5cfabe

Please sign in to comment.