0
# Parser Transformation
1
2
Instance methods for transforming and chaining parsers. These methods enable building complex parsing pipelines by combining and transforming simpler parsers.
3
4
## Capabilities
5
6
### Result Transformation
7
8
Methods that transform the result of a successful parse.
9
10
```javascript { .api }
11
/**
12
* Transform parser result using a function
13
* @param {Function} fn - Function to transform the result
14
* @returns {Parser} Parser with transformed result
15
*/
16
parser.map(fn);
17
18
/**
19
* Replace parser result with a constant value
20
* @param {any} value - Value to use as result
21
* @returns {Parser} Parser that returns the constant value
22
*/
23
parser.result(value);
24
25
/**
26
* Transform input before parsing (contravariant map)
27
* @param {Function} fn - Function to transform input
28
* @returns {Parser} Parser that operates on transformed input
29
*/
30
parser.contramap(fn);
31
32
/**
33
* Transform both input and output
34
* @param {Function} f - Function to transform input
35
* @param {Function} g - Function to transform output
36
* @returns {Parser} Parser with both transformations
37
*/
38
parser.promap(f, g);
39
```
40
41
**Usage Examples:**
42
43
```javascript
44
// Basic transformation
45
const number = Parsimmon.digits.map(Number);
46
number.parse("42"); // { status: true, value: 42 }
47
48
// Transform to constant
49
const trueParser = Parsimmon.string("yes").result(true);
50
trueParser.parse("yes"); // { status: true, value: true }
51
52
// Transform input
53
const caseInsensitive = Parsimmon.string("hello")
54
.contramap(input => input.toLowerCase());
55
caseInsensitive.parse("HELLO"); // { status: true, value: "hello" }
56
57
// Transform both input and output
58
const upperDigits = Parsimmon.digits
59
.promap(
60
input => input.toLowerCase(), // normalize input
61
result => result.toUpperCase() // transform output
62
);
63
64
// Complex transformations
65
const coordinate = Parsimmon.seqMap(
66
Parsimmon.digits.map(Number),
67
Parsimmon.string(","),
68
Parsimmon.digits.map(Number),
69
(x, _, y) => ({ x, y, distance: Math.sqrt(x*x + y*y) })
70
);
71
```
72
73
### String Manipulation
74
75
Methods for working with string results.
76
77
```javascript { .api }
78
/**
79
* Concatenate array result to single string
80
* @returns {Parser} Parser that returns joined string
81
*/
82
parser.tie();
83
84
/**
85
* Concatenate array result with separator
86
* @param {string} separator - String to join array elements
87
* @returns {Parser} Parser that returns joined string
88
*/
89
parser.tieWith(separator);
90
```
91
92
**Usage Examples:**
93
94
```javascript
95
// Join characters
96
const letters = Parsimmon.letter.many().tie();
97
letters.parse("hello"); // { status: true, value: "hello" }
98
99
// Join with separator
100
const words = Parsimmon.letters.sepBy(Parsimmon.whitespace).tieWith(" ");
101
words.parse("hello world"); // { status: true, value: "hello world" }
102
103
// Parse hex bytes
104
const hexPair = Parsimmon.regexp(/[0-9a-fA-F]/).times(2).tie();
105
const hexString = hexPair.sepBy(Parsimmon.string(" ")).tieWith("");
106
hexString.parse("A1 B2 C3"); // { status: true, value: "A1B2C3" }
107
```
108
109
### Parser Chaining
110
111
Methods that chain parsers together in sequence or conditionally.
112
113
```javascript { .api }
114
/**
115
* Monadic bind - chain dependent parsers
116
* @param {Function} fn - Function that returns next parser based on result
117
* @returns {Parser} Parser with conditional chaining
118
*/
119
parser.chain(fn);
120
121
/**
122
* Parse this parser then another, return second result
123
* @param {Parser} next - Parser to run after this one
124
* @returns {Parser} Parser that returns result of next
125
*/
126
parser.then(next);
127
128
/**
129
* Parse this parser then another, return first result
130
* @param {Parser} next - Parser to run after this one
131
* @returns {Parser} Parser that returns result of this
132
*/
133
parser.skip(next);
134
```
135
136
**Usage Examples:**
137
138
```javascript
139
// Conditional parsing with chain
140
const numberParser = Parsimmon.digits.chain(function(digits) {
141
const num = Number(digits);
142
if (num > 100) {
143
return Parsimmon.fail("number too large");
144
}
145
return Parsimmon.succeed(num);
146
});
147
148
// Dynamic parsing based on first result
149
const taggedValue = Parsimmon.letters.chain(function(tag) {
150
if (tag === "num") {
151
return Parsimmon.string(":").then(Parsimmon.digits.map(Number));
152
} else if (tag === "str") {
153
return Parsimmon.string(":").then(Parsimmon.regexp(/[^,]*/));
154
} else {
155
return Parsimmon.fail("unknown tag: " + tag);
156
}
157
});
158
159
// Sequence with then/skip
160
const assignment = Parsimmon.letters
161
.skip(Parsimmon.string("=").trim(Parsimmon.optWhitespace))
162
.then(Parsimmon.digits.map(Number));
163
164
assignment.parse("x = 42"); // { status: true, value: 42 }
165
166
// Parse between delimiters
167
const quoted = Parsimmon.string('"')
168
.then(Parsimmon.regexp(/[^"]*/))
169
.skip(Parsimmon.string('"'));
170
```
171
172
### Parser Combination
173
174
Methods that combine this parser with alternatives or conditions.
175
176
```javascript { .api }
177
/**
178
* Try alternative parser if this one fails
179
* @param {Parser} alternative - Parser to try if this fails
180
* @returns {Parser} Parser that tries alternative on failure
181
*/
182
parser.or(alternative);
183
184
/**
185
* Provide fallback value if parser fails
186
* @param {any} value - Value to return on failure
187
* @returns {Parser} Parser with fallback value
188
*/
189
parser.fallback(value);
190
191
/**
192
* Assert condition on result, fail with message if false
193
* @param {Function} condition - Predicate function for result
194
* @param {string} message - Error message if condition fails
195
* @returns {Parser} Parser with assertion
196
*/
197
parser.assert(condition, message);
198
```
199
200
**Usage Examples:**
201
202
```javascript
203
// Try alternatives
204
const booleanValue = Parsimmon.string("true")
205
.result(true)
206
.or(Parsimmon.string("false").result(false));
207
208
// Provide defaults
209
const optionalName = Parsimmon.string("name:")
210
.then(Parsimmon.letters)
211
.fallback("anonymous");
212
213
// Validate results
214
const positiveNumber = Parsimmon.digits
215
.map(Number)
216
.assert(n => n > 0, "positive number required");
217
218
// Complex validation
219
const email = Parsimmon.regexp(/[^@]+@[^@]+\.[^@]+/)
220
.assert(s => s.includes("."), "email must contain domain")
221
.assert(s => s.length < 100, "email too long");
222
```
223
224
### Repetition Methods
225
226
Methods that repeat a parser multiple times.
227
228
```javascript { .api }
229
/**
230
* Repeat parser zero or more times
231
* @returns {Parser} Parser that returns array of results
232
*/
233
parser.many();
234
235
/**
236
* Repeat parser exactly n times, or between min and max times
237
* @param {number} min - Minimum repetitions (or exact if max not given)
238
* @param {number} [max] - Maximum repetitions
239
* @returns {Parser} Parser that returns array of results
240
*/
241
parser.times(min, max);
242
243
/**
244
* Repeat parser at most n times
245
* @param {number} n - Maximum repetitions
246
* @returns {Parser} Parser that returns array of results
247
*/
248
parser.atMost(n);
249
250
/**
251
* Repeat parser at least n times
252
* @param {number} n - Minimum repetitions
253
* @returns {Parser} Parser that returns array of results
254
*/
255
parser.atLeast(n);
256
```
257
258
**Usage Examples:**
259
260
```javascript
261
// Parse multiple items
262
const numbers = Parsimmon.digits.map(Number).many();
263
numbers.parse("123456"); // { status: true, value: [1,2,3,4,5,6] }
264
265
// Exact repetition
266
const hexByte = Parsimmon.regexp(/[0-9a-fA-F]/).times(2).tie();
267
hexByte.parse("a1"); // { status: true, value: "a1" }
268
269
// Range repetition
270
const identifier = Parsimmon.regexp(/[a-zA-Z_]/)
271
.then(Parsimmon.regexp(/[a-zA-Z0-9_]/).times(0, 63))
272
.map(([first, rest]) => first + rest.join(""));
273
274
// At least/at most
275
const someDigits = Parsimmon.digit.atLeast(1).tie();
276
const fewDigits = Parsimmon.digit.atMost(3).tie();
277
278
// Parse comma-separated with repetition
279
const csvRow = Parsimmon.regexp(/[^,\n]*/)
280
.sepBy(Parsimmon.string(","))
281
.skip(Parsimmon.newline.atMost(1));
282
```
283
284
### Separated Repetition
285
286
Instance methods for parsing lists with separators.
287
288
```javascript { .api }
289
/**
290
* Parse zero or more of this parser separated by separator
291
* @param {Parser} separator - Parser for separator
292
* @returns {Parser} Parser that returns array of results
293
*/
294
parser.sepBy(separator);
295
296
/**
297
* Parse one or more of this parser separated by separator
298
* @param {Parser} separator - Parser for separator
299
* @returns {Parser} Parser that returns array of results
300
*/
301
parser.sepBy1(separator);
302
```
303
304
**Usage Examples:**
305
306
```javascript
307
// Parse word lists
308
const wordList = Parsimmon.letters.sepBy(Parsimmon.string(","));
309
wordList.parse("apple,banana,cherry"); // { status: true, value: ["apple","banana","cherry"] }
310
311
// Parse with required elements
312
const nonEmptyList = Parsimmon.digits.map(Number).sepBy1(Parsimmon.string("|"));
313
nonEmptyList.parse("1|2|3"); // { status: true, value: [1,2,3] }
314
nonEmptyList.parse(""); // { status: false, ... }
315
316
// Parse with complex separators
317
const parameter = Parsimmon.letters;
318
const paramList = parameter.sepBy(
319
Parsimmon.string(",").trim(Parsimmon.optWhitespace)
320
);
321
```
322
323
### String Manipulation
324
325
Methods for working with array results as strings.
326
327
```javascript { .api }
328
/**
329
* Join array of strings with separator
330
* @param {string} separator - String to join with
331
* @returns {Parser} Parser that returns joined string
332
*/
333
parser.tieWith(separator);
334
335
/**
336
* Join array of strings with no separator
337
* @returns {Parser} Parser that returns concatenated string
338
*/
339
parser.tie();
340
```
341
342
**Usage Examples:**
343
344
```javascript
345
// Collect characters into string
346
const word = Parsimmon.letter.many().tie();
347
word.parse("hello"); // { status: true, value: "hello" }
348
349
// Join with separator
350
const spacedWord = Parsimmon.letter.many().tieWith(" ");
351
spacedWord.parse("hello"); // { status: true, value: "h e l l o" }
352
353
// Parse dotted identifiers
354
const dottedId = Parsimmon.letters.sepBy1(Parsimmon.string(".")).tieWith(".");
355
dottedId.parse("foo.bar.baz"); // { status: true, value: "foo.bar.baz" }
356
```
357
358
### Position and Context
359
360
Methods for working with parsing position and context.
361
362
```javascript { .api }
363
/**
364
* Mark result with start and end positions
365
* @returns {Parser} Parser that returns {start, value, end}
366
*/
367
parser.mark();
368
369
/**
370
* Create AST node with name and position
371
* @param {string} name - Node name
372
* @returns {Parser} Parser that returns {name, value, start, end}
373
*/
374
parser.node(name);
375
376
/**
377
* Apply wrapper function to this parser
378
* @param {Function} wrapper - Function that takes and returns a parser
379
* @returns {Parser} Result of wrapper function
380
*/
381
parser.thru(wrapper);
382
```
383
384
**Usage Examples:**
385
386
```javascript
387
// Track positions
388
const markedNumber = Parsimmon.digits.map(Number).mark();
389
markedNumber.parse("123");
390
// { status: true, value: { start: {offset:0, line:1, col:1}, value: 123, end: {offset:3, line:1, col:4} } }
391
392
// Create AST nodes
393
const numberNode = Parsimmon.digits.map(Number).node("Number");
394
numberNode.parse("42");
395
// { status: true, value: { name: "Number", value: 42, start: {...}, end: {...} } }
396
397
// Apply transformations
398
const trimmed = Parsimmon.string("hello").thru(p =>
399
Parsimmon.optWhitespace.then(p).skip(Parsimmon.optWhitespace)
400
);
401
402
// Reusable wrapper
403
const token = (parser) => parser.trim(Parsimmon.optWhitespace);
404
const numberToken = Parsimmon.digits.map(Number).thru(token);
405
```
406
407
### Lookahead and Validation
408
409
Methods for conditional parsing and validation.
410
411
```javascript { .api }
412
/**
413
* Ensure parser is followed by another parser
414
* @param {Parser|string|RegExp} x - Parser, string, or regex that should follow
415
* @returns {Parser} Parser that checks lookahead
416
*/
417
parser.lookahead(x);
418
419
/**
420
* Ensure parser is not followed by another parser
421
* @param {Parser} x - Parser that should not follow
422
* @returns {Parser} Parser that checks negative lookahead
423
*/
424
parser.notFollowedBy(x);
425
426
/**
427
* Wrap parser between left and right parsers
428
* @param {Parser} left - Parser for left delimiter
429
* @param {Parser} right - Parser for right delimiter
430
* @returns {Parser} Parser that returns middle value
431
*/
432
parser.wrap(left, right);
433
434
/**
435
* Trim parser with whitespace (or specified parser)
436
* @param {Parser} [parser] - Parser to use for trimming (default: optWhitespace)
437
* @returns {Parser} Parser with trimming
438
*/
439
parser.trim(parser);
440
441
/**
442
* Provide custom error description
443
* @param {string|string[]} description - Error description
444
* @returns {Parser} Parser with custom error messages
445
*/
446
parser.desc(description);
447
```
448
449
**Usage Examples:**
450
451
```javascript
452
// Lookahead validation
453
const keyword = Parsimmon.string("if")
454
.notFollowedBy(Parsimmon.regexp(/[a-zA-Z0-9_]/));
455
456
keyword.parse("if"); // Success
457
keyword.parse("ifdef"); // Fails
458
459
// Wrap between delimiters
460
const parenthesized = Parsimmon.digits.wrap(
461
Parsimmon.string("("),
462
Parsimmon.string(")")
463
);
464
parenthesized.parse("(123)"); // { status: true, value: "123" }
465
466
// Trim whitespace
467
const trimmedNumber = Parsimmon.digits.trim(Parsimmon.optWhitespace);
468
trimmedNumber.parse(" 42 "); // { status: true, value: "42" }
469
470
// Custom error messages
471
const email = Parsimmon.regexp(/[^@]+@[^@]+/)
472
.desc("valid email address");
473
474
email.parse("invalid"); // Error mentions "valid email address"
475
```