-
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);
};