Skip to content

Commit 2122883

Browse files
authored
feat: Void matcher for handlers (#107)
* feat: Void matcher for handlers * Bump to 1.1.16
1 parent 7586e1a commit 2122883

File tree

4 files changed

+61
-2
lines changed

4 files changed

+61
-2
lines changed

lib/query/builder.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import {
66
SeqMatcher,
77
SymMatcher,
88
} from './matchers';
9-
import { BeginMatcher, EndMatcher } from './matchers/anchor-matcher';
9+
import {
10+
BeginMatcher,
11+
EndMatcher,
12+
VoidMatcher,
13+
} from './matchers/anchor-matcher';
1014
import { CommentMatcher } from './matchers/comment-matcher';
1115
import { NumMatcher } from './matchers/num-matcher';
1216
import {
@@ -173,6 +177,12 @@ abstract class AbstractBuilder<Ctx> extends TerminalBuilder<Ctx> {
173177
const builder = new SeqBuilder(this, other);
174178
return builder;
175179
}
180+
181+
handler(fn: (context: Ctx) => Ctx): SeqBuilder<Ctx> {
182+
const voidMatcher = new VoidBuilder(fn);
183+
const builder = new SeqBuilder(this, voidMatcher);
184+
return builder;
185+
}
176186
}
177187

178188
// Anchors
@@ -200,6 +210,20 @@ export function begin<Ctx>(): BeginBuilder<Ctx> {
200210
return new BeginBuilder<Ctx>();
201211
}
202212

213+
class VoidBuilder<Ctx> extends AbstractBuilder<Ctx> {
214+
constructor(private readonly fn: (context: Ctx) => Ctx) {
215+
super();
216+
}
217+
218+
build(): VoidMatcher<Ctx> {
219+
return new VoidMatcher<Ctx>(this.fn);
220+
}
221+
}
222+
223+
export function handler<Ctx>(fn: (context: Ctx) => Ctx): VoidBuilder<Ctx> {
224+
return new VoidBuilder(fn);
225+
}
226+
203227
// Sequence
204228

205229
class SeqBuilder<Ctx> extends AbstractBuilder<Ctx> {

lib/query/matchers/anchor-matcher.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,14 @@ describe('query/matchers/anchor-matcher', () => {
3030
const res = lang.query(input, query, []);
3131
expect(res).toBeUndefined();
3232
});
33+
34+
it('supports handler builder', () => {
35+
const input = 'baz';
36+
const query = q
37+
.handler<Ctx>((ctx) => [...ctx, 'bar'])
38+
.sym(handler)
39+
.handler((ctx) => ctx.map((x) => x.toUpperCase()));
40+
const res = lang.query(input, query, ['foo']);
41+
expect(res).toEqual(['FOO', 'BAR', 'BAZ']);
42+
});
3343
});

lib/query/matchers/anchor-matcher.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Cursor } from '../../parser';
2+
import { clone } from '../../util/clone';
13
import type { Checkpoint } from '../types';
24
import { AbstractMatcher } from './abstract-matcher';
35

@@ -26,3 +28,26 @@ export class EndMatcher<Ctx> extends AbstractMatcher<Ctx> {
2628
return null;
2729
}
2830
}
31+
32+
export class VoidMatcher<Ctx> extends AbstractMatcher<Ctx> {
33+
private handler: (context: Ctx) => Ctx;
34+
35+
constructor(handler: (context: Ctx) => Ctx) {
36+
super();
37+
this.handler = (context: Ctx) => handler(clone(context));
38+
}
39+
40+
match(checkpoint: Checkpoint<Ctx>): Checkpoint<Ctx> | null {
41+
const { cursor, context } = checkpoint;
42+
const newContext = this.handler(context);
43+
return { cursor, context: newContext };
44+
}
45+
46+
override moveRight(cursor: Cursor): Cursor {
47+
return cursor;
48+
}
49+
50+
override seekNext(cursor: Cursor): Cursor {
51+
return cursor;
52+
}
53+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "good-enough-parser",
33
"description": "Parse and query computer programs source code",
4-
"version": "1.1.15",
4+
"version": "1.1.16",
55
"repository": "git@github.com:zharinov/good-enough-parser.git",
66
"author": "Sergei Zharinov",
77
"contributors": [

0 commit comments

Comments
 (0)