0
# Parser Combinators
1
2
Functions that combine multiple parsers into more complex parsing patterns. Combinators are the core building blocks for creating sophisticated parsers from simpler components.
3
4
## Capabilities
5
6
### Sequence Combinators
7
8
Combinators that parse multiple elements in sequence.
9
10
```javascript { .api }
11
/**
12
* Parses all given parsers in sequence, returns array of results
13
* @param {...Parser} parsers - Parsers to run in sequence
14
* @returns {Parser} Parser that returns array of all results
15
*/
16
Parsimmon.seq(p1, p2, ...pn);
17
18
/**
19
* Parses all parsers in sequence, applies mapper function to results
20
* @param {...Parser} parsers - Parsers to run in sequence
21
* @param {Function} mapper - Function applied to results array
22
* @returns {Parser} Parser that returns mapped result
23
*/
24
Parsimmon.seqMap(p1, p2, ...pn, mapper);
25
26
/**
27
* Parses parsers in sequence, returns object with named results
28
* @param {...(Parser|Array)} args - Parsers or [name, parser] pairs
29
* @returns {Parser} Parser that returns object with named properties
30
*/
31
Parsimmon.seqObj(...args);
32
```
33
34
**Usage Examples:**
35
36
```javascript
37
// Basic sequence - returns array
38
const pair = Parsimmon.seq(
39
Parsimmon.digits,
40
Parsimmon.string(","),
41
Parsimmon.digits
42
);
43
pair.parse("12,34"); // { status: true, value: ["12", ",", "34"] }
44
45
// Sequence with transformation
46
const coordinate = Parsimmon.seqMap(
47
Parsimmon.digits.map(Number),
48
Parsimmon.string(","),
49
Parsimmon.digits.map(Number),
50
(x, comma, y) => ({ x, y })
51
);
52
coordinate.parse("10,20"); // { status: true, value: { x: 10, y: 20 } }
53
54
// Named sequence - returns object
55
const point = Parsimmon.seqObj(
56
["x", Parsimmon.digits.map(Number)],
57
Parsimmon.string(","),
58
["y", Parsimmon.digits.map(Number)]
59
);
60
point.parse("15,25"); // { status: true, value: { x: 15, y: 25 } }
61
```
62
63
### Alternative Combinators
64
65
Combinators that try multiple parsing alternatives.
66
67
```javascript { .api }
68
/**
69
* Tries parsers in order, returns result of first success
70
* @param {...Parser} parsers - Parsers to try in order
71
* @returns {Parser} Parser that succeeds with first successful alternative
72
*/
73
Parsimmon.alt(p1, p2, ...pn);
74
```
75
76
**Usage Examples:**
77
78
```javascript
79
// Parse different number formats
80
const number = Parsimmon.alt(
81
Parsimmon.regexp(/0x[0-9a-fA-F]+/).map(s => parseInt(s, 16)), // hex
82
Parsimmon.regexp(/0b[01]+/).map(s => parseInt(s.slice(2), 2)), // binary
83
Parsimmon.regexp(/[0-9]+/).map(Number) // decimal
84
);
85
86
number.parse("42"); // { status: true, value: 42 }
87
number.parse("0x2A"); // { status: true, value: 42 }
88
number.parse("0b101010"); // { status: true, value: 42 }
89
90
// Parse keywords or identifiers
91
const keywordOrId = Parsimmon.alt(
92
Parsimmon.string("if"),
93
Parsimmon.string("else"),
94
Parsimmon.string("while"),
95
Parsimmon.regexp(/[a-zA-Z][a-zA-Z0-9]*/)
96
);
97
```
98
99
### Separated Combinators
100
101
Combinators for parsing lists with separators.
102
103
```javascript { .api }
104
/**
105
* Parses zero or more content items separated by separator
106
* @param {Parser} content - Parser for list items
107
* @param {Parser} separator - Parser for separator between items
108
* @returns {Parser} Parser that returns array of content items
109
*/
110
Parsimmon.sepBy(content, separator);
111
112
/**
113
* Parses one or more content items separated by separator
114
* @param {Parser} content - Parser for list items
115
* @param {Parser} separator - Parser for separator between items
116
* @returns {Parser} Parser that returns array of content items
117
*/
118
Parsimmon.sepBy1(content, separator);
119
```
120
121
**Usage Examples:**
122
123
```javascript
124
// Parse comma-separated numbers
125
const numberList = Parsimmon.sepBy(
126
Parsimmon.digits.map(Number),
127
Parsimmon.string(",").trim(Parsimmon.optWhitespace)
128
);
129
130
numberList.parse("1,2,3"); // { status: true, value: [1, 2, 3] }
131
numberList.parse("42"); // { status: true, value: [42] }
132
numberList.parse(""); // { status: true, value: [] }
133
134
// Parse at least one item
135
const nonEmptyList = Parsimmon.sepBy1(
136
Parsimmon.letters,
137
Parsimmon.string("|")
138
);
139
140
nonEmptyList.parse("a|b|c"); // { status: true, value: ["a", "b", "c"] }
141
nonEmptyList.parse(""); // { status: false, ... }
142
143
// Parse function arguments
144
const args = Parsimmon.string("(")
145
.then(Parsimmon.sepBy(
146
Parsimmon.regexp(/[^,)]+/).trim(Parsimmon.optWhitespace),
147
Parsimmon.string(",")
148
))
149
.skip(Parsimmon.string(")"));
150
151
args.parse("(a, b, c)"); // { status: true, value: ["a", "b", "c"] }
152
```
153
154
### Lazy Evaluation
155
156
Combinators for handling recursive grammars and forward references.
157
158
```javascript { .api }
159
/**
160
* Creates a lazy parser for recursive grammars
161
* @param {Function} fn - Function that returns a parser
162
* @returns {Parser} Parser that evaluates lazily
163
*/
164
Parsimmon.lazy(fn);
165
166
/**
167
* Creates a lazy parser with description
168
* @param {string} description - Description for error messages
169
* @param {Function} fn - Function that returns a parser
170
* @returns {Parser} Parser that evaluates lazily
171
*/
172
Parsimmon.lazy(description, fn);
173
```
174
175
**Usage Examples:**
176
177
```javascript
178
// Recursive JSON-like structure
179
const value = Parsimmon.lazy(() => Parsimmon.alt(
180
Parsimmon.regexp(/"[^"]*"/).map(s => s.slice(1, -1)), // string
181
Parsimmon.digits.map(Number), // number
182
array,
183
object
184
));
185
186
const array = Parsimmon.string("[")
187
.then(Parsimmon.sepBy(value, Parsimmon.string(",").trim(Parsimmon.optWhitespace)))
188
.skip(Parsimmon.string("]"));
189
190
const object = Parsimmon.string("{")
191
.then(Parsimmon.sepBy(
192
Parsimmon.seqMap(
193
Parsimmon.regexp(/"[^"]*"/).map(s => s.slice(1, -1)),
194
Parsimmon.string(":").trim(Parsimmon.optWhitespace),
195
value,
196
(key, colon, val) => [key, val]
197
),
198
Parsimmon.string(",").trim(Parsimmon.optWhitespace)
199
))
200
.skip(Parsimmon.string("}"))
201
.map(pairs => Object.fromEntries(pairs));
202
203
// Arithmetic expressions with precedence
204
const expr = Parsimmon.lazy("expression", () => addition);
205
206
const factor = Parsimmon.alt(
207
Parsimmon.digits.map(Number),
208
Parsimmon.string("(").then(expr).skip(Parsimmon.string(")"))
209
);
210
211
const term = Parsimmon.sepBy1(factor, Parsimmon.string("*"))
212
.map(factors => factors.reduce((a, b) => a * b));
213
214
const addition = Parsimmon.sepBy1(term, Parsimmon.string("+"))
215
.map(terms => terms.reduce((a, b) => a + b));
216
```
217
218
### Language Creation
219
220
Framework for creating complete parsing languages with interdependent rules.
221
222
```javascript { .api }
223
/**
224
* Creates a language object with interdependent parsers
225
* @param {Object} parsers - Object with parser-generating functions
226
* @returns {Object} Language object with parser properties
227
*/
228
Parsimmon.createLanguage(parsers);
229
```
230
231
**Usage Examples:**
232
233
```javascript
234
// Create a simple expression language
235
const MathLang = Parsimmon.createLanguage({
236
// Basic tokens
237
number: () => Parsimmon.regexp(/[0-9]+/).map(Number),
238
operator: () => Parsimmon.oneOf("+-*/"),
239
240
// Expressions using other rules
241
factor: (r) => Parsimmon.alt(
242
r.number,
243
Parsimmon.string("(").then(r.expr).skip(Parsimmon.string(")"))
244
),
245
246
term: (r) => Parsimmon.seqMap(
247
r.factor,
248
Parsimmon.seq(Parsimmon.oneOf("*/"), r.factor).many(),
249
(first, rest) => rest.reduce((acc, [op, val]) =>
250
op === "*" ? acc * val : acc / val, first)
251
),
252
253
expr: (r) => Parsimmon.seqMap(
254
r.term,
255
Parsimmon.seq(Parsimmon.oneOf("+-"), r.term).many(),
256
(first, rest) => rest.reduce((acc, [op, val]) =>
257
op === "+" ? acc + val : acc - val, first)
258
)
259
});
260
261
// Use the language
262
MathLang.expr.parse("2 + 3 * 4"); // { status: true, value: 14 }
263
264
// JSON parser using createLanguage
265
const JsonLang = Parsimmon.createLanguage({
266
value: (r) => Parsimmon.alt(
267
r.string, r.number, r.boolean, r.null, r.array, r.object
268
),
269
270
string: () => Parsimmon.regexp(/"((?:\\.|[^"\\])*)"/,1),
271
number: () => Parsimmon.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/).map(Number),
272
boolean: () => Parsimmon.alt(Parsimmon.string("true"), Parsimmon.string("false")).map(x => x === "true"),
273
null: () => Parsimmon.string("null").result(null),
274
275
array: (r) => Parsimmon.string("[")
276
.then(r.value.sepBy(Parsimmon.string(",").trim(Parsimmon.optWhitespace)))
277
.skip(Parsimmon.string("]")),
278
279
object: (r) => Parsimmon.string("{")
280
.then(Parsimmon.seqMap(
281
r.string,
282
Parsimmon.string(":").trim(Parsimmon.optWhitespace),
283
r.value,
284
(key, _, value) => [key, value]
285
).sepBy(Parsimmon.string(",").trim(Parsimmon.optWhitespace)))
286
.skip(Parsimmon.string("}"))
287
.map(pairs => Object.fromEntries(pairs))
288
});
289
```