pytex/PyTeX/format/macros.py

216 lines
6.8 KiB
Python
Raw Normal View History

2022-02-06 19:36:37 +01:00
import re
from typing import List, Union, Tuple, Dict
2022-02-07 18:36:30 +01:00
2022-02-06 19:36:37 +01:00
from .constants import *
from .enums import FormatterProperty, Argument, FormatterMode
from abc import ABC, abstractmethod
2022-02-17 22:31:44 +01:00
from .errors import *
2022-02-08 16:24:42 +01:00
class MacroReplacement:
2022-02-06 19:36:37 +01:00
def __init__(
self,
replacement: str,
*args,
**kwargs,
2022-02-06 19:36:37 +01:00
):
if 'format_type' in kwargs.keys():
self.format_type = kwargs['format_type']
else:
self.format_type = '%'
2022-02-06 19:36:37 +01:00
self.replacement: str = replacement
self.args = args
self.kwargs = kwargs
def make_format_args(self, formatter, *call_args) -> Tuple[Tuple, Dict]:
2022-02-06 19:36:37 +01:00
new_args = []
for arg in self.args:
if type(arg) == FormatterProperty:
try:
new_args.append(formatter.attribute_dict[arg.value])
2022-02-06 19:36:37 +01:00
except:
raise NotImplementedError
elif type(arg) == Argument:
try:
2022-02-06 19:40:41 +01:00
new_args.append(call_args[arg.value - 1])
2022-02-06 19:36:37 +01:00
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]
2022-02-06 19:36:37 +01:00
elif type(self.kwargs[kw]) == Argument:
2022-02-06 19:40:41 +01:00
new_kwargs[kw] = call_args[self.kwargs[kw].value - 1]
2022-02-06 19:36:37 +01:00
elif type(self.kwargs[kw]) == str:
new_kwargs[kw] = self.kwargs[kw]
else:
raise NotImplementedError
return tuple(new_args), new_kwargs
2022-02-06 19:36:37 +01:00
def format(self, formatter, *call_args) -> str:
args, kwargs = self.make_format_args(formatter, *call_args)
if self.format_type == '%':
if self.kwargs:
2022-02-06 19:36:37 +01:00
raise NotImplementedError # Currently, not supported
return self.replacement % args
2022-02-06 19:36:37 +01:00
elif self.format_type == '{':
return self.replacement.format(
*args, **kwargs, **formatter.attribute_dict
2022-02-06 19:36:37 +01:00
)
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
2022-02-06 19:36:37 +01:00
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
2022-02-17 20:38:25 +01:00
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
2022-02-17 20:38:25 +01:00
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]]:
2022-02-17 22:31:44 +01:00
if formatter.mode not in [FormatterMode.normal, FormatterMode.meta]:
raise NotImplementedError # invalid config begin
formatter.mode = FormatterMode.drop
return []
2022-02-17 20:38:25 +01:00
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 []
2022-02-17 20:38:25 +01:00
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:
2022-02-17 23:16:41 +01:00
raise PyTeXInvalidBeginMacroCodeUsageError(
r"\begin{macrocode} used outside meta context"
)
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:
2022-02-17 23:16:41 +01:00
raise PyTeXInvalidEndMacroCodeUsageError(
r"\end{macrocode} used outside macrocode context"
)
formatter.mode = FormatterMode.meta
return r'% \end{macrocode}'
class ArgumentMacro(Macro):
2022-02-06 19:36:37 +01:00
def __init__(
self,
macroname: str,
2022-02-06 19:36:37 +01:00
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}(?<!@)\)'.format(
keyword=FORMATTER_PREFIX + self.macroname,
arguments=','.join(['(.*?)'] * self.num_args)
))
def matches(self, line: str) -> bool:
if line.find('!!') != -1:
pass
2022-02-06 19:36:37 +01:00
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]:
2022-02-06 19:36:37 +01:00
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