0
# Utility Functions
1
2
Helper functions and constants that support parsy's parsing operations. These utilities provide common functionality used throughout the parsing library.
3
4
## Capabilities
5
6
### Position Information
7
8
Get line and column information for positions in input streams.
9
10
```python { .api }
11
def line_info_at(stream, index):
12
"""
13
Get line and column information for a position in the stream.
14
15
Args:
16
stream: Input string to analyze
17
index: Position index in the stream
18
19
Returns:
20
tuple: (line, column) where both are 0-indexed
21
22
Raises:
23
ValueError: If index is greater than stream length
24
"""
25
```
26
27
### Identity Function
28
29
No-operation function that returns its argument unchanged.
30
31
```python { .api }
32
noop: function
33
"""Identity function (lambda x: x) used as default transform parameter."""
34
```
35
36
## Usage Examples
37
38
### Position Tracking
39
40
```python
41
from parsy import line_info_at
42
43
# Get position information
44
text = "line1\nline2\nline3"
45
position = 8 # After "line1\nli"
46
47
line, col = line_info_at(text, position)
48
print(f"Position {position} is at line {line}, column {col}")
49
# Output: Position 8 is at line 1, column 2
50
51
# Use in error reporting
52
def parse_with_position_info(parser, text):
53
try:
54
return parser.parse(text)
55
except ParseError as e:
56
line, col = line_info_at(text, e.index)
57
print(f"Parse error at line {line}, column {col}: {e}")
58
raise
59
60
# Track positions during parsing
61
@generate
62
def positioned_content():
63
start_info = yield line_info
64
content = yield regex(r'[^\n]+')
65
end_info = yield line_info
66
return {
67
'content': content,
68
'start': start_info,
69
'end': end_info
70
}
71
```
72
73
### Transform Functions
74
75
```python
76
from parsy import string, noop
77
78
# Using noop as identity transform
79
case_sensitive = string('Hello', transform=noop)
80
result = case_sensitive.parse('Hello') # Returns 'Hello'
81
82
# Custom transform functions
83
case_insensitive = string('Hello', transform=str.lower)
84
result = case_insensitive.parse('HELLO') # Returns 'Hello'
85
86
# Transform function for normalization
87
def normalize_whitespace(s):
88
return ' '.join(s.split())
89
90
normalized_string = string('hello world', transform=normalize_whitespace)
91
result = normalized_string.parse('hello world') # Returns 'hello world'
92
```
93
94
### Position-Aware Error Handling
95
96
```python
97
from parsy import generate, string, regex, ParseError, line_info_at
98
99
class DetailedParseError(Exception):
100
def __init__(self, message, line, column, context=""):
101
self.message = message
102
self.line = line
103
self.column = column
104
self.context = context
105
super().__init__(f"{message} at line {line}, column {column}")
106
107
def enhanced_parse(parser, text):
108
"""Parse with enhanced error reporting."""
109
try:
110
return parser.parse(text)
111
except ParseError as e:
112
line, col = line_info_at(text, e.index)
113
114
# Get context around error position
115
lines = text.split('\n')
116
if line < len(lines):
117
context = lines[line]
118
pointer = ' ' * col + '^'
119
context_display = f"{context}\n{pointer}"
120
else:
121
context_display = ""
122
123
raise DetailedParseError(
124
str(e),
125
line + 1, # Convert to 1-indexed for human reading
126
col + 1, # Convert to 1-indexed for human reading
127
context_display
128
)
129
130
# Usage example
131
@generate
132
def simple_assignment():
133
var_name = yield regex(r'[a-zA-Z_][a-zA-Z0-9_]*')
134
yield string('=')
135
value = yield regex(r'\d+').map(int)
136
return (var_name, value)
137
138
try:
139
result = enhanced_parse(simple_assignment, 'x = abc')
140
except DetailedParseError as e:
141
print(f"Error: {e}")
142
print(f"Context:\n{e.context}")
143
```
144
145
## Line Information Utilities
146
147
```python
148
from parsy import line_info, generate, string, any_char
149
150
# Create a parser that tracks line information
151
@generate
152
def line_tracking_parser():
153
content = []
154
155
while True:
156
# Try to get current position
157
pos = yield line_info
158
159
# Try to get next character
160
try:
161
char = yield any_char
162
content.append((char, pos))
163
except:
164
break
165
166
return content
167
168
# Parse with line tracking
169
text = "a\nb\nc"
170
result = line_tracking_parser.parse_partial(text)
171
# Returns [('a', (0, 0)), ('\n', (0, 1)), ('b', (1, 0)), ('\n', (1, 1)), ('c', (2, 0))]
172
```