17db96d56Sopenharmony_ciimport re 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_cifrom ..info import KIND, ParsedItem, FileInfo 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ciclass TextInfo: 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ci def __init__(self, text, start=None, end=None): 97db96d56Sopenharmony_ci # immutable: 107db96d56Sopenharmony_ci if not start: 117db96d56Sopenharmony_ci start = 1 127db96d56Sopenharmony_ci self.start = start 137db96d56Sopenharmony_ci 147db96d56Sopenharmony_ci # mutable: 157db96d56Sopenharmony_ci lines = text.splitlines() or [''] 167db96d56Sopenharmony_ci self.text = text.strip() 177db96d56Sopenharmony_ci if not end: 187db96d56Sopenharmony_ci end = start + len(lines) - 1 197db96d56Sopenharmony_ci self.end = end 207db96d56Sopenharmony_ci self.line = lines[-1] 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ci def __repr__(self): 237db96d56Sopenharmony_ci args = (f'{a}={getattr(self, a)!r}' 247db96d56Sopenharmony_ci for a in ['text', 'start', 'end']) 257db96d56Sopenharmony_ci return f'{type(self).__name__}({", ".join(args)})' 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci def add_line(self, line, lno=None): 287db96d56Sopenharmony_ci if lno is None: 297db96d56Sopenharmony_ci lno = self.end + 1 307db96d56Sopenharmony_ci else: 317db96d56Sopenharmony_ci if isinstance(lno, FileInfo): 327db96d56Sopenharmony_ci fileinfo = lno 337db96d56Sopenharmony_ci if fileinfo.filename != self.filename: 347db96d56Sopenharmony_ci raise NotImplementedError((fileinfo, self.filename)) 357db96d56Sopenharmony_ci lno = fileinfo.lno 367db96d56Sopenharmony_ci # XXX 377db96d56Sopenharmony_ci #if lno < self.end: 387db96d56Sopenharmony_ci # raise NotImplementedError((lno, self.end)) 397db96d56Sopenharmony_ci line = line.lstrip() 407db96d56Sopenharmony_ci self.text += ' ' + line 417db96d56Sopenharmony_ci self.line = line 427db96d56Sopenharmony_ci self.end = lno 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ciclass SourceInfo: 467db96d56Sopenharmony_ci 477db96d56Sopenharmony_ci _ready = False 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ci def __init__(self, filename, _current=None): 507db96d56Sopenharmony_ci # immutable: 517db96d56Sopenharmony_ci self.filename = filename 527db96d56Sopenharmony_ci # mutable: 537db96d56Sopenharmony_ci if isinstance(_current, str): 547db96d56Sopenharmony_ci _current = TextInfo(_current) 557db96d56Sopenharmony_ci self._current = _current 567db96d56Sopenharmony_ci start = -1 577db96d56Sopenharmony_ci self._start = _current.start if _current else -1 587db96d56Sopenharmony_ci self._nested = [] 597db96d56Sopenharmony_ci self._set_ready() 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_ci def __repr__(self): 627db96d56Sopenharmony_ci args = (f'{a}={getattr(self, a)!r}' 637db96d56Sopenharmony_ci for a in ['filename', '_current']) 647db96d56Sopenharmony_ci return f'{type(self).__name__}({", ".join(args)})' 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_ci @property 677db96d56Sopenharmony_ci def start(self): 687db96d56Sopenharmony_ci if self._current is None: 697db96d56Sopenharmony_ci return self._start 707db96d56Sopenharmony_ci return self._current.start 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_ci @property 737db96d56Sopenharmony_ci def end(self): 747db96d56Sopenharmony_ci if self._current is None: 757db96d56Sopenharmony_ci return self._start 767db96d56Sopenharmony_ci return self._current.end 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci @property 797db96d56Sopenharmony_ci def text(self): 807db96d56Sopenharmony_ci if self._current is None: 817db96d56Sopenharmony_ci return '' 827db96d56Sopenharmony_ci return self._current.text 837db96d56Sopenharmony_ci 847db96d56Sopenharmony_ci def nest(self, text, before, start=None): 857db96d56Sopenharmony_ci if self._current is None: 867db96d56Sopenharmony_ci raise Exception('nesting requires active source text') 877db96d56Sopenharmony_ci current = self._current 887db96d56Sopenharmony_ci current.text = before 897db96d56Sopenharmony_ci self._nested.append(current) 907db96d56Sopenharmony_ci self._replace(text, start) 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ci def resume(self, remainder=None): 937db96d56Sopenharmony_ci if not self._nested: 947db96d56Sopenharmony_ci raise Exception('no nested text to resume') 957db96d56Sopenharmony_ci if self._current is None: 967db96d56Sopenharmony_ci raise Exception('un-nesting requires active source text') 977db96d56Sopenharmony_ci if remainder is None: 987db96d56Sopenharmony_ci remainder = self._current.text 997db96d56Sopenharmony_ci self._clear() 1007db96d56Sopenharmony_ci self._current = self._nested.pop() 1017db96d56Sopenharmony_ci self._current.text += ' ' + remainder 1027db96d56Sopenharmony_ci self._set_ready() 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci def advance(self, remainder, start=None): 1057db96d56Sopenharmony_ci if self._current is None: 1067db96d56Sopenharmony_ci raise Exception('advancing requires active source text') 1077db96d56Sopenharmony_ci if remainder.strip(): 1087db96d56Sopenharmony_ci self._replace(remainder, start, fixnested=True) 1097db96d56Sopenharmony_ci else: 1107db96d56Sopenharmony_ci if self._nested: 1117db96d56Sopenharmony_ci self._replace('', start, fixnested=True) 1127db96d56Sopenharmony_ci #raise Exception('cannot advance while nesting') 1137db96d56Sopenharmony_ci else: 1147db96d56Sopenharmony_ci self._clear(start) 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ci def resolve(self, kind, data, name, parent=None): 1177db96d56Sopenharmony_ci # "field" isn't a top-level kind, so we leave it as-is. 1187db96d56Sopenharmony_ci if kind and kind != 'field': 1197db96d56Sopenharmony_ci kind = KIND._from_raw(kind) 1207db96d56Sopenharmony_ci fileinfo = FileInfo(self.filename, self._start) 1217db96d56Sopenharmony_ci return ParsedItem(fileinfo, kind, parent, name, data) 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci def done(self): 1247db96d56Sopenharmony_ci self._set_ready() 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci def too_much(self, maxtext, maxlines): 1277db96d56Sopenharmony_ci if maxtext and len(self.text) > maxtext: 1287db96d56Sopenharmony_ci pass 1297db96d56Sopenharmony_ci elif maxlines and self.end - self.start > maxlines: 1307db96d56Sopenharmony_ci pass 1317db96d56Sopenharmony_ci else: 1327db96d56Sopenharmony_ci return False 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci #if re.fullmatch(r'[^;]+\[\][ ]*=[ ]*[{]([ ]*\d+,)*([ ]*\d+,?)\s*', 1357db96d56Sopenharmony_ci # self._current.text): 1367db96d56Sopenharmony_ci # return False 1377db96d56Sopenharmony_ci return True 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci def _set_ready(self): 1407db96d56Sopenharmony_ci if self._current is None: 1417db96d56Sopenharmony_ci self._ready = False 1427db96d56Sopenharmony_ci else: 1437db96d56Sopenharmony_ci self._ready = self._current.text.strip() != '' 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci def _used(self): 1467db96d56Sopenharmony_ci ready = self._ready 1477db96d56Sopenharmony_ci self._ready = False 1487db96d56Sopenharmony_ci return ready 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_ci def _clear(self, start=None): 1517db96d56Sopenharmony_ci old = self._current 1527db96d56Sopenharmony_ci if self._current is not None: 1537db96d56Sopenharmony_ci # XXX Fail if self._current wasn't used up? 1547db96d56Sopenharmony_ci if start is None: 1557db96d56Sopenharmony_ci start = self._current.end 1567db96d56Sopenharmony_ci self._current = None 1577db96d56Sopenharmony_ci if start is not None: 1587db96d56Sopenharmony_ci self._start = start 1597db96d56Sopenharmony_ci self._set_ready() 1607db96d56Sopenharmony_ci return old 1617db96d56Sopenharmony_ci 1627db96d56Sopenharmony_ci def _replace(self, text, start=None, *, fixnested=False): 1637db96d56Sopenharmony_ci end = self._current.end 1647db96d56Sopenharmony_ci old = self._clear(start) 1657db96d56Sopenharmony_ci self._current = TextInfo(text, self._start, end) 1667db96d56Sopenharmony_ci if fixnested and self._nested and self._nested[-1] is old: 1677db96d56Sopenharmony_ci self._nested[-1] = self._current 1687db96d56Sopenharmony_ci self._set_ready() 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci def _add_line(self, line, lno=None): 1717db96d56Sopenharmony_ci if not line.strip(): 1727db96d56Sopenharmony_ci # We don't worry about multi-line string literals. 1737db96d56Sopenharmony_ci return 1747db96d56Sopenharmony_ci if self._current is None: 1757db96d56Sopenharmony_ci self._start = lno 1767db96d56Sopenharmony_ci self._current = TextInfo(line, lno) 1777db96d56Sopenharmony_ci else: 1787db96d56Sopenharmony_ci # XXX 1797db96d56Sopenharmony_ci #if lno < self._current.end: 1807db96d56Sopenharmony_ci # # A circular include? 1817db96d56Sopenharmony_ci # raise NotImplementedError((lno, self)) 1827db96d56Sopenharmony_ci self._current.add_line(line, lno) 1837db96d56Sopenharmony_ci self._ready = True 184