Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/c_parser/parser/_info.py
12 views
1
#import re
2
3
from ..info import KIND, ParsedItem, FileInfo
4
5
6
class TextInfo:
7
8
def __init__(self, text, start=None, end=None):
9
# immutable:
10
if not start:
11
start = 1
12
self.start = start
13
14
# mutable:
15
lines = text.splitlines() or ['']
16
self.text = text.strip()
17
if not end:
18
end = start + len(lines) - 1
19
self.end = end
20
self.line = lines[-1]
21
22
def __repr__(self):
23
args = (f'{a}={getattr(self, a)!r}'
24
for a in ['text', 'start', 'end'])
25
return f'{type(self).__name__}({", ".join(args)})'
26
27
def add_line(self, line, lno=None):
28
if lno is None:
29
lno = self.end + 1
30
else:
31
if isinstance(lno, FileInfo):
32
fileinfo = lno
33
if fileinfo.filename != self.filename:
34
raise NotImplementedError((fileinfo, self.filename))
35
lno = fileinfo.lno
36
# XXX
37
#if lno < self.end:
38
# raise NotImplementedError((lno, self.end))
39
line = line.lstrip()
40
self.text += ' ' + line
41
self.line = line
42
self.end = lno
43
44
45
class SourceInfo:
46
47
_ready = False
48
49
def __init__(self, filename, _current=None):
50
# immutable:
51
self.filename = filename
52
# mutable:
53
if isinstance(_current, str):
54
_current = TextInfo(_current)
55
self._current = _current
56
start = -1
57
self._start = _current.start if _current else -1
58
self._nested = []
59
self._set_ready()
60
61
def __repr__(self):
62
args = (f'{a}={getattr(self, a)!r}'
63
for a in ['filename', '_current'])
64
return f'{type(self).__name__}({", ".join(args)})'
65
66
@property
67
def start(self):
68
if self._current is None:
69
return self._start
70
return self._current.start
71
72
@property
73
def end(self):
74
if self._current is None:
75
return self._start
76
return self._current.end
77
78
@property
79
def text(self):
80
if self._current is None:
81
return ''
82
return self._current.text
83
84
def nest(self, text, before, start=None):
85
if self._current is None:
86
raise Exception('nesting requires active source text')
87
current = self._current
88
current.text = before
89
self._nested.append(current)
90
self._replace(text, start)
91
92
def resume(self, remainder=None):
93
if not self._nested:
94
raise Exception('no nested text to resume')
95
if self._current is None:
96
raise Exception('un-nesting requires active source text')
97
if remainder is None:
98
remainder = self._current.text
99
self._clear()
100
self._current = self._nested.pop()
101
self._current.text += ' ' + remainder
102
self._set_ready()
103
104
def advance(self, remainder, start=None):
105
if self._current is None:
106
raise Exception('advancing requires active source text')
107
if remainder.strip():
108
self._replace(remainder, start, fixnested=True)
109
else:
110
if self._nested:
111
self._replace('', start, fixnested=True)
112
#raise Exception('cannot advance while nesting')
113
else:
114
self._clear(start)
115
116
def resolve(self, kind, data, name, parent=None):
117
# "field" isn't a top-level kind, so we leave it as-is.
118
if kind and kind != 'field':
119
kind = KIND._from_raw(kind)
120
fileinfo = FileInfo(self.filename, self._start)
121
return ParsedItem(fileinfo, kind, parent, name, data)
122
123
def done(self):
124
self._set_ready()
125
126
def too_much(self, maxtext, maxlines):
127
if maxtext and len(self.text) > maxtext:
128
pass
129
elif maxlines and self.end - self.start > maxlines:
130
pass
131
else:
132
return False
133
134
#if re.fullmatch(r'[^;]+\[\][ ]*=[ ]*[{]([ ]*\d+,)*([ ]*\d+,?)\s*',
135
# self._current.text):
136
# return False
137
return True
138
139
def _set_ready(self):
140
if self._current is None:
141
self._ready = False
142
else:
143
self._ready = self._current.text.strip() != ''
144
145
def _used(self):
146
ready = self._ready
147
self._ready = False
148
return ready
149
150
def _clear(self, start=None):
151
old = self._current
152
if self._current is not None:
153
# XXX Fail if self._current wasn't used up?
154
if start is None:
155
start = self._current.end
156
self._current = None
157
if start is not None:
158
self._start = start
159
self._set_ready()
160
return old
161
162
def _replace(self, text, start=None, *, fixnested=False):
163
end = self._current.end
164
old = self._clear(start)
165
self._current = TextInfo(text, self._start, end)
166
if fixnested and self._nested and self._nested[-1] is old:
167
self._nested[-1] = self._current
168
self._set_ready()
169
170
def _add_line(self, line, lno=None):
171
if not line.strip():
172
# We don't worry about multi-line string literals.
173
return
174
if self._current is None:
175
self._start = lno
176
self._current = TextInfo(line, lno)
177
else:
178
# XXX
179
#if lno < self._current.end:
180
# # A circular include?
181
# raise NotImplementedError((lno, self))
182
self._current.add_line(line, lno)
183
self._ready = True
184
185