Source code for pyndoc.readers.parser

import importlib
from pyndoc.ast.ast_tree import ASTTree


[docs] class Parser: """Class representing a general reader for all input languages :param lang: The reader's language :type lang: ``str`` """ def __init__(self, lang: str) -> None: self._tree = ASTTree([]) #: The current AST Tree (blocks already read) self.context = [] #: The context stack self.token = "" #: Token currently matched lang_module = importlib.import_module(f"pyndoc.readers.{lang}.tokens") self._block_types = ( lang_module.declared_tokens.keys() ) #: Types of available composite blocks (declared by lang) self._atom_block_types = lang_module.declared_atomic_patterns.keys() #: Atom block types (declared by lang) self._atom_wrapper_block = ( lang_module.atom_wrapper ) #: The block in which atom blocks will be wrapped if no context is found lang_module.assign_patterns()
[docs] def check_atom_block(self) -> None: """Check if an atom block has ended. That is, if matching it with a next character results in None (but previously matched) """ for atom_block in self._atom_block_types: match_cur, self.token = atom_block.match_pattern(text=self.token, context=self.context) match_prev, _ = atom_block.match_pattern(text=self.token[:-1], context=self.context) if not match_cur and match_prev: old_token, self.token = self.token[:-1], self.token[-1:] self._process_atom_block(old_token)
def _process_atom_block(self, token: str) -> None: """process an atom block (Str, Space etc.) :param token: The token to be processed :type token: ``str`` """ atom_block = [ atom_block for atom_block in self._atom_block_types if atom_block.match_pattern(text=token, context=self.context)[0] ] if not atom_block: return if not self.context: self.context.append(self._atom_wrapper_block()) args = tuple([token]) if atom_block[0].block_has_content() else () self.context[-1].insert(atom_block[0](*args))
[docs] def check_end(self) -> None: """check if the current context block has ended""" if len(self.context): end_match, new_token = self.context[-1].end(token=self.token, context=self.context) else: end_match, new_token = self._atom_wrapper_block.end(token=self.token, context=self.context) if not end_match: self.token = new_token return # process token before the block-end self._process_atom_block(self.token[: end_match.start()]) self.token = new_token self._end()
def _end(self) -> None: """Move a processed blocks to the finished tree""" if len(self.context) > 1: item = self.context.pop() self.context[-1].insert(item) elif self.context: self._tree.append(self.context.pop())
[docs] def check_start(self) -> None: """Check if a new block has just started. If so, set the current context as the block """ for block in self._block_types: start_match, new_token = block.start(token=self.token, context=self.context) if not start_match: self.token = new_token continue if block.is_inline() and not self.context: self.context.append(self._atom_wrapper_block()) else: self._process_atom_block(self.token[: start_match.start()]) self.token = new_token self.context.append(block()) self.context[-1].process_read(match=start_match, context=self.context) break
[docs] def close_context(self) -> None: """ If the file has ended - go through each block in the context and end it """ while self.context: self.token = self.context[-1].handle_premature_closure(token=self.token, context=self.context) self.process_trailing_atom() self._end()
[docs] def process_trailing_atom(self) -> None: """ Immediately process an atom block without checking for the usual condition """ self._process_atom_block(self.token) self.token = ""