import re from typing import List, Union, Tuple, Dict from .constants import * from .enums import FormatterProperty, Argument, FormatterMode from abc import ABC, abstractmethod from .errors import * class MacroReplacement: def __init__( self, replacement: str, *args, **kwargs, ): if 'format_type' in kwargs.keys(): self.format_type = kwargs['format_type'] else: self.format_type = '%' self.replacement: str = replacement self.args = args self.kwargs = kwargs def make_format_args(self, formatter, *call_args) -> Tuple[Tuple, Dict]: new_args = [] for arg in self.args: if type(arg) == FormatterProperty: try: new_args.append(formatter.attribute_dict[arg.value]) except: raise NotImplementedError elif type(arg) == Argument: try: new_args.append(call_args[arg.value - 1]) except: raise NotImplementedError elif type(arg) == str: new_args.append(arg) else: raise NotImplementedError new_kwargs = {} for kw in self.kwargs.keys(): if type(self.kwargs[kw]) == FormatterProperty: new_kwargs[kw] = formatter.attribute_dict[self.kwargs[kw].value] elif type(self.kwargs[kw]) == Argument: new_kwargs[kw] = call_args[self.kwargs[kw].value - 1] elif type(self.kwargs[kw]) == str: new_kwargs[kw] = self.kwargs[kw] else: raise NotImplementedError return tuple(new_args), new_kwargs def format(self, formatter, *call_args) -> str: args, kwargs = self.make_format_args(formatter, *call_args) if self.format_type == '%': if self.kwargs: raise NotImplementedError # Currently, not supported return self.replacement % args elif self.format_type == '{': return self.replacement.format( *args, **kwargs, **formatter.attribute_dict ) else: raise NotImplementedError class Macro(ABC): @abstractmethod def __init__(self): raise NotImplementedError @abstractmethod def matches(self, line: str) -> bool: raise NotImplementedError @abstractmethod def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]: """ :param line: Line where macro matches :param formatter: :return: First: replacement. Second: indicates direct shipout if True """ raise NotImplementedError class SimpleMacro(Macro): def __init__( self, macroname: str, macro_replacement: MacroReplacement ): self.macroname = macroname self.macro_replacement = macro_replacement def matches(self, line: str) -> bool: return line.find(FORMATTER_PREFIX + self.macroname) != -1 def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]: return line.replace( FORMATTER_PREFIX + self.macroname, self.macro_replacement.format( formatter )), False class SingleLineMacro(Macro, ABC): def __init__( self, strip: str = ' %\n' ): self.strip = strip @abstractmethod def _apply(self, line, formatter) -> Union[Union[str, List[str]], Tuple[Union[str, List[str]], bool]]: pass def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]: replacement = self._apply(line, formatter) if isinstance(replacement, tuple): return replacement else: return replacement, True @abstractmethod def _matches(self, line: str) -> Optional[str]: pass def matches(self, line: str) -> bool: match = self._matches(line.strip(self.strip)) if match is None: return False else: if not line.strip(self.strip) == match: raise NotImplementedError return True class RegexSingleLineMacro(SingleLineMacro, ABC): def __init__( self, regex: str, strip: str = ' %\n' ): self.regex = regex super(RegexSingleLineMacro, self).__init__(strip) def _matches(self, line: str) -> Optional[str]: match = re.search(self.regex, line) if match is not None: return match.group() else: return None class SimpleSingleLineMacro(SingleLineMacro, ABC): def __init__( self, chars: str, strip: str = ' %\n' ): self.chars = chars super(SimpleSingleLineMacro, self).__init__(strip) def _matches(self, line: str) -> Optional[str]: return self.chars if self.chars in line else None class GuardMacro(RegexSingleLineMacro): def __init__(self): super(GuardMacro, self).__init__(r'<(\*|/|@@=)[a-zA-Z_]*>') def _apply(self, line, formatter) -> Union[str, List[str]]: match = re.search(self.regex, line) return '%' + match.group() class ConfigBeginMacro(SimpleSingleLineMacro): def __init__(self): super(ConfigBeginMacro, self).__init__(FORMATTER_PREFIX + INFILE_CONFIG_BEGIN_CONFIG) def _apply(self, line: str, formatter) -> Union[str, List[str]]: if formatter.mode.is_drop(): raise NotImplementedError # invalid config begin formatter.mode = formatter.mode.to_drop() return [] class ConfigEndMacro(SimpleSingleLineMacro): def __init__(self): super(ConfigEndMacro, self).__init__(FORMATTER_PREFIX + INFILE_CONFIG_END_CONFIG) def _apply(self, line: str, formatter) -> Union[str, List[str]]: if not formatter.mode.is_drop(): raise NotImplementedError # invalid formatter.mode = formatter.mode.to_undrop() return [] class ImplementationBeginMacro(SimpleSingleLineMacro): def __init__(self): super(ImplementationBeginMacro, self).__init__(FORMATTER_PREFIX + IMPLEMENTATION_BEGIN_MACRO) def _apply(self, line, formatter) -> Tuple[Union[str, List[str]], bool]: return [ r'% \begin{implementation}', r'', r'\section{\pkg{!name} implementation}', r'\begin{macrocode}', r'<*!outtype>', r'\end{macrocode}', r'', r'\begin{macrocode}', r'<@@=!!>', r'\end{macrocode}', ], False class ImplementationEndMacro(SimpleSingleLineMacro): def __init__(self): super(ImplementationEndMacro, self).__init__(FORMATTER_PREFIX + IMPLEMENTATION_END_MACRO) def _apply(self, line, formatter) -> Tuple[Union[str, List[str]], bool]: return [ r'\begin{macrocode}', r'', r'\end{macrocode}', r'', r'% \end{implementation}' ], False class MacroCodeBeginMacro(SimpleSingleLineMacro): def __init__(self): super(MacroCodeBeginMacro, self).__init__(r'\begin{macrocode}') def _apply(self, line: str, formatter) -> Union[str, List[str]]: if not formatter.mode == FormatterMode.meta: raise PyTeXInvalidBeginMacroCodeUsageError( r"\begin{macrocode} used outside meta context" ) formatter.mode = FormatterMode.macrocode return r'% \begin{macrocode}' class MacroCodeEndMacro(SimpleSingleLineMacro): def __init__(self): super(MacroCodeEndMacro, self).__init__(r'\end{macrocode}') def _apply(self, line: str, formatter) -> Union[str, List[str]]: if not formatter.mode == FormatterMode.macrocode: raise PyTeXInvalidEndMacroCodeUsageError( r"\end{macrocode} used outside macrocode context" ) formatter.mode = FormatterMode.meta return r'% \end{macrocode}' class ArgumentMacro(Macro): def __init__( self, macroname: str, num_args: int, macro_replacement: MacroReplacement ): self.macroname = macroname self.num_args = num_args self.macro_replacement: MacroReplacement = macro_replacement self._search_regex = re.compile(r'{keyword}\({arguments}(? bool: if line.find('!!') != -1: pass match = re.search(self._search_regex, line) if match is None: return False else: return True def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]: match = re.search(self._search_regex, line) if match is None: raise NotImplementedError replacement = self.macro_replacement.format( formatter, match.groups() ) return line.replace( match.group(), replacement ), False