import re from typing import List, Union, Tuple, Dict from .constants import * from .enums import FormatterProperty, Argument, FormatterMode from abc import ABC, abstractmethod 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, chars: str, strip: str = ' %\n' ): self.chars = chars self.strip = strip def matches(self, line: str) -> bool: if line.find(self.chars) != -1: if not line.strip(self.strip) == self.chars: raise NotImplementedError return True else: return False @abstractmethod def _apply(self, line, formatter) -> Union[str, List[str]]: pass def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]: return self._apply(line, formatter), True class ConfigBeginMacro(SingleLineMacro): def __init__(self): super(ConfigBeginMacro, self).__init__(FORMATTER_PREFIX + INFILE_CONFIG_BEGIN_CONFIG) def _apply(self, line: str, formatter) -> Union[str, List[str]]: if not formatter.mode == FormatterMode.normal: raise NotImplementedError # invalid config begin formatter.mode = FormatterMode.drop return [] class ConfigEndMacro(SingleLineMacro): 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 == FormatterMode.drop: raise NotImplementedError # invalid formatter.mode = FormatterMode.normal return [] class MacroCodeBeginMacro(SingleLineMacro): 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 NotImplementedError formatter.mode = FormatterMode.macrocode return r'% \begin{macrocode}' class MacroCodeEndMacro(SingleLineMacro): 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 NotImplementedError 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