0
# Parser Combinators
1
2
Higher-order functions for combining and repeating parsers. These combinators enable building complex parsers from simple building blocks through composition and repetition patterns, forming the foundation of parser combinator methodology.
3
4
## Capabilities
5
6
### Repetition Combinators
7
8
Control how many times a parser should be applied, with various bounds and greedy matching behavior.
9
10
```python { .api }
11
def times(p, mint, maxt=None):
12
"""
13
Repeat a parser between mint and maxt times (greedy).
14
15
Args:
16
p (Parser): Parser to repeat
17
mint (int): Minimum number of repetitions
18
maxt (int, optional): Maximum repetitions. Defaults to mint if not specified
19
20
Returns:
21
Parser: Parser that returns list of values from p
22
23
Note:
24
Does as much matching as possible within bounds.
25
"""
26
27
def count(p, n):
28
"""
29
Parse exactly n occurrences of parser p.
30
31
Args:
32
p (Parser): Parser to repeat
33
n (int): Exact number of repetitions required
34
35
Returns:
36
Parser: Parser that returns list of exactly n values
37
38
Note:
39
If n <= 0, returns parser that succeeds with empty list.
40
"""
41
42
def many(p):
43
"""
44
Repeat a parser 0 to infinity times (greedy).
45
46
Args:
47
p (Parser): Parser to repeat
48
49
Returns:
50
Parser: Parser that returns list of values (possibly empty)
51
52
Note:
53
Always succeeds, returns empty list if no matches.
54
"""
55
56
def many1(p):
57
"""
58
Repeat a parser 1 to infinity times (greedy).
59
60
Args:
61
p (Parser): Parser to repeat
62
63
Returns:
64
Parser: Parser that returns non-empty list of values
65
66
Raises:
67
ParseError: If parser p fails on first attempt
68
"""
69
```
70
71
## Usage Examples
72
73
### Basic Repetition
74
75
```python
76
from parsec import times, count, many, many1, letter, digit, string
77
78
# Parse exactly 3 letters
79
three_letters = count(letter(), 3)
80
result = three_letters.parse("abcdef") # Returns ['a', 'b', 'c']
81
82
# Parse 2-4 letters
83
some_letters = times(letter(), 2, 4)
84
result = some_letters.parse("abcdef") # Returns ['a', 'b', 'c', 'd']
85
86
# Parse zero or more digits
87
digits = many(digit())
88
result = digits.parse("123abc") # Returns ['1', '2', '3']
89
result = digits.parse("abc") # Returns [] (empty list)
90
91
# Parse one or more letters
92
letters = many1(letter())
93
result = letters.parse("abc123") # Returns ['a', 'b', 'c']
94
try:
95
result = letters.parse("123") # Raises ParseError
96
except ParseError:
97
print("No letters found")
98
```
99
100
### Combining with Other Parsers
101
102
```python
103
from parsec import times, many, string, letter, digit
104
105
# Parse word followed by number
106
word_num = many1(letter()) + many1(digit())
107
result = word_num.parse("abc123") # Returns "abc123"
108
109
# Parse 3 letters followed by a digit
110
pattern = times(letter(), 3) >> digit()
111
result = pattern.parse("xyz1abc") # Returns '1'
112
113
# Parse comma-separated letters
114
@generate
115
def csv_letters():
116
first = yield letter()
117
rest = yield many(string(",") >> letter())
118
return [first] + rest
119
120
result = csv_letters.parse("a,b,c") # Returns ['a', 'b', 'c']
121
```
122
123
### Flexible Repetition Bounds
124
125
```python
126
from parsec import times, letter
127
128
# Minimum only - parse at least 2 letters
129
at_least_two = times(letter(), 2, float('inf'))
130
result = at_least_two.parse("abcdefgh") # Returns ['a','b','c','d','e','f','g','h']
131
132
# Zero repetitions allowed
133
zero_or_more = times(letter(), 0, 3)
134
result = zero_or_more.parse("12345") # Returns [] (empty list)
135
result = zero_or_more.parse("ab12") # Returns ['a', 'b']
136
137
# Fixed range
138
bounded = times(letter(), 2, 4)
139
result = bounded.parse("a") # ParseError - not enough matches
140
result = bounded.parse("abcd") # Returns ['a', 'b', 'c', 'd']
141
result = bounded.parse("abcdefg") # Returns ['a', 'b', 'c', 'd'] (stops at max)
142
```
143
144
### Greedy Behavior
145
146
```python
147
from parsec import many, letter, digit, string
148
149
# Greedy matching - takes as much as possible
150
parser = many(letter()) >> digit()
151
152
# This works - many() stops when it can't match more letters
153
result = parser.parse("abc1") # Returns '1'
154
155
# This also works - many() consumes all letters it can
156
result = parser.parse("abcdefg1") # Returns '1'
157
158
# many() is greedy but will backtrack to allow overall success
159
parser = many(string("ab")) >> string("ab")
160
result = parser.parse("ababab") # many() takes "abab", leaves "ab" for final parser
161
```