mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			183 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#import re
 | 
						|
 | 
						|
from ..info import KIND, ParsedItem, FileInfo
 | 
						|
 | 
						|
 | 
						|
class TextInfo:
 | 
						|
 | 
						|
    def __init__(self, text, start=None, end=None):
 | 
						|
        # immutable:
 | 
						|
        if not start:
 | 
						|
            start = 1
 | 
						|
        self.start = start
 | 
						|
 | 
						|
        # mutable:
 | 
						|
        lines = text.splitlines() or ['']
 | 
						|
        self.text = text.strip()
 | 
						|
        if not end:
 | 
						|
            end = start + len(lines) - 1
 | 
						|
        self.end = end
 | 
						|
        self.line = lines[-1]
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        args = (f'{a}={getattr(self, a)!r}'
 | 
						|
                for a in ['text', 'start', 'end'])
 | 
						|
        return f'{type(self).__name__}({", ".join(args)})'
 | 
						|
 | 
						|
    def add_line(self, line, lno=None):
 | 
						|
        if lno is None:
 | 
						|
            lno = self.end + 1
 | 
						|
        else:
 | 
						|
            if isinstance(lno, FileInfo):
 | 
						|
                fileinfo = lno
 | 
						|
                if fileinfo.filename != self.filename:
 | 
						|
                    raise NotImplementedError((fileinfo, self.filename))
 | 
						|
                lno = fileinfo.lno
 | 
						|
            # XXX
 | 
						|
            #if lno < self.end:
 | 
						|
            #    raise NotImplementedError((lno, self.end))
 | 
						|
        line = line.lstrip()
 | 
						|
        self.text += ' ' + line
 | 
						|
        self.line = line
 | 
						|
        self.end = lno
 | 
						|
 | 
						|
 | 
						|
class SourceInfo:
 | 
						|
 | 
						|
    _ready = False
 | 
						|
 | 
						|
    def __init__(self, filename, _current=None):
 | 
						|
        # immutable:
 | 
						|
        self.filename = filename
 | 
						|
        # mutable:
 | 
						|
        if isinstance(_current, str):
 | 
						|
            _current = TextInfo(_current)
 | 
						|
        self._current = _current
 | 
						|
        start = -1
 | 
						|
        self._start = _current.start if _current else -1
 | 
						|
        self._nested = []
 | 
						|
        self._set_ready()
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        args = (f'{a}={getattr(self, a)!r}'
 | 
						|
                for a in ['filename', '_current'])
 | 
						|
        return f'{type(self).__name__}({", ".join(args)})'
 | 
						|
 | 
						|
    @property
 | 
						|
    def start(self):
 | 
						|
        if self._current is None:
 | 
						|
            return self._start
 | 
						|
        return self._current.start
 | 
						|
 | 
						|
    @property
 | 
						|
    def end(self):
 | 
						|
        if self._current is None:
 | 
						|
            return self._start
 | 
						|
        return self._current.end
 | 
						|
 | 
						|
    @property
 | 
						|
    def text(self):
 | 
						|
        if self._current is None:
 | 
						|
            return ''
 | 
						|
        return self._current.text
 | 
						|
 | 
						|
    def nest(self, text, before, start=None):
 | 
						|
        if self._current is None:
 | 
						|
            raise Exception('nesting requires active source text')
 | 
						|
        current = self._current
 | 
						|
        current.text = before
 | 
						|
        self._nested.append(current)
 | 
						|
        self._replace(text, start)
 | 
						|
 | 
						|
    def resume(self, remainder=None):
 | 
						|
        if not self._nested:
 | 
						|
            raise Exception('no nested text to resume')
 | 
						|
        if self._current is None:
 | 
						|
            raise Exception('un-nesting requires active source text')
 | 
						|
        if remainder is None:
 | 
						|
            remainder = self._current.text
 | 
						|
        self._clear()
 | 
						|
        self._current = self._nested.pop()
 | 
						|
        self._current.text += ' ' + remainder
 | 
						|
        self._set_ready()
 | 
						|
 | 
						|
    def advance(self, remainder, start=None):
 | 
						|
        if self._current is None:
 | 
						|
            raise Exception('advancing requires active source text')
 | 
						|
        if remainder.strip():
 | 
						|
            self._replace(remainder, start, fixnested=True)
 | 
						|
        else:
 | 
						|
            if self._nested:
 | 
						|
                self._replace('', start, fixnested=True)
 | 
						|
                #raise Exception('cannot advance while nesting')
 | 
						|
            else:
 | 
						|
                self._clear(start)
 | 
						|
 | 
						|
    def resolve(self, kind, data, name, parent=None):
 | 
						|
        # "field" isn't a top-level kind, so we leave it as-is.
 | 
						|
        if kind and kind != 'field':
 | 
						|
            kind = KIND._from_raw(kind)
 | 
						|
        fileinfo = FileInfo(self.filename, self._start)
 | 
						|
        return ParsedItem(fileinfo, kind, parent, name, data)
 | 
						|
 | 
						|
    def done(self):
 | 
						|
        self._set_ready()
 | 
						|
 | 
						|
    def too_much(self, maxtext, maxlines):
 | 
						|
        if maxtext and len(self.text) > maxtext:
 | 
						|
            pass
 | 
						|
        elif maxlines and self.end - self.start > maxlines:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            return False
 | 
						|
 | 
						|
        #if re.fullmatch(r'[^;]+\[\][ ]*=[ ]*[{]([ ]*\d+,)*([ ]*\d+,?)\s*',
 | 
						|
        #                self._current.text):
 | 
						|
        #    return False
 | 
						|
        return True
 | 
						|
 | 
						|
    def _set_ready(self):
 | 
						|
        if self._current is None:
 | 
						|
            self._ready = False
 | 
						|
        else:
 | 
						|
            self._ready = self._current.text.strip() != ''
 | 
						|
 | 
						|
    def _used(self):
 | 
						|
        ready = self._ready
 | 
						|
        self._ready = False
 | 
						|
        return ready
 | 
						|
 | 
						|
    def _clear(self, start=None):
 | 
						|
        old = self._current
 | 
						|
        if self._current is not None:
 | 
						|
            # XXX Fail if self._current wasn't used up?
 | 
						|
            if start is None:
 | 
						|
                start = self._current.end
 | 
						|
            self._current = None
 | 
						|
        if start is not None:
 | 
						|
            self._start = start
 | 
						|
        self._set_ready()
 | 
						|
        return old
 | 
						|
 | 
						|
    def _replace(self, text, start=None, *, fixnested=False):
 | 
						|
        end = self._current.end
 | 
						|
        old = self._clear(start)
 | 
						|
        self._current = TextInfo(text, self._start, end)
 | 
						|
        if fixnested and self._nested and self._nested[-1] is old:
 | 
						|
            self._nested[-1] = self._current
 | 
						|
        self._set_ready()
 | 
						|
 | 
						|
    def _add_line(self, line, lno=None):
 | 
						|
        if not line.strip():
 | 
						|
            # We don't worry about multi-line string literals.
 | 
						|
            return
 | 
						|
        if self._current is None:
 | 
						|
            self._start = lno
 | 
						|
            self._current = TextInfo(line, lno)
 | 
						|
        else:
 | 
						|
            # XXX
 | 
						|
            #if lno < self._current.end:
 | 
						|
            #    # A circular include?
 | 
						|
            #    raise NotImplementedError((lno, self))
 | 
						|
            self._current.add_line(line, lno)
 | 
						|
        self._ready = True
 |