-
Notifications
You must be signed in to change notification settings - Fork 8
/
combinator.ts
98 lines (79 loc) · 2.03 KB
/
combinator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { Node } from "./types/Node.ts";
import { Parser, ParseText } from "./types/Parser.ts";
export const or = (parsers: Parser[]): Parser => {
const { length } = parsers;
return (text, position, rootParser) => {
for (let i = 0; i < length; i++) {
const match = parsers[i](text, position, rootParser);
if (match) {
return match;
}
}
return null;
};
};
export const oneOrMore = <A extends Node, B extends Node>(
parser: Parser<A>,
andThen: (nodes: readonly A[]) => B,
): Parser<B> => {
const rec = (
text: string,
position: number,
rootParser: ParseText,
): readonly NonNullable<ReturnType<typeof parser>>[] => {
const match = parser(text, position, rootParser);
if (!match) {
return [];
}
const [, nextPosition] = match;
return [match, ...rec(text, nextPosition, rootParser)];
};
return (text, position, rootParser) => {
const ret = rec(text, position, rootParser);
if (ret.length === 0) {
return null;
}
const [, lastPosition] = ret[ret.length - 1];
return [andThen(ret.map(([a]) => a)), lastPosition];
};
};
export const regexp = <T extends Node = Node>(
pattern: RegExp,
callback: (
match: string[],
text: string,
position: number,
parseText: ParseText,
) => [T, number] | null,
): Parser<T> =>
(text, position, parseText) => {
const match = text.substring(position).match(pattern);
if (!match) {
return null;
}
return callback(match, text, position, parseText);
};
export const explicit = (parser: Parser): Parser =>
(
text,
position,
parseText,
) => {
const prevChar = text.charAt(position - 1);
if (prevChar && !prevChar.match(/[\s.,([{!?\-=]/)) {
return null;
}
return parser(text, position, parseText);
};
export const topOfLine =
<T extends Node = Node>(parser: Parser<T>): Parser<T> =>
(
text,
position,
parseText,
) => {
if (position > 0 && text.charAt(position - 1) !== "\n") {
return null;
}
return parser(text, position, parseText);
};