2022-02-06 19:36:37 +01:00
|
|
|
import re
|
2022-02-09 17:22:38 +01:00
|
|
|
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 *
|
2022-02-09 19:39:27 +01:00
|
|
|
from .enums import FormatterProperty, Argument, FormatterMode
|
2022-02-09 17:22:38 +01:00
|
|
|
from abc import ABC, abstractmethod
|
2022-02-17 22:31:44 +01:00
|
|
|
from .errors import *
|
2022-02-06 18:34:39 +01:00
|
|
|
|
|
|
|
|
2022-02-08 16:24:42 +01:00
|
|
|
class MacroReplacement:
|
2022-02-06 19:36:37 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
replacement: str,
|
|
|
|
*args,
|
2022-02-09 17:22:38 +01:00
|
|
|
**kwargs,
|
2022-02-06 19:36:37 +01:00
|
|
|
):
|
2022-02-09 17:22:38 +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
|
|
|
|
|
2022-02-09 17:22:38 +01:00
|
|
|
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:
|
2022-02-09 15:46:10 +01:00
|
|
|
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:
|
2022-02-09 17:22:38 +01:00
|
|
|
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
|
2022-02-09 17:22:38 +01:00
|
|
|
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 == '%':
|
2022-02-09 17:22:38 +01:00
|
|
|
if self.kwargs:
|
2022-02-06 19:36:37 +01:00
|
|
|
raise NotImplementedError # Currently, not supported
|
2022-02-09 17:22:38 +01:00
|
|
|
return self.replacement % args
|
2022-02-06 19:36:37 +01:00
|
|
|
elif self.format_type == '{':
|
|
|
|
return self.replacement.format(
|
2022-02-09 17:22:38 +01:00
|
|
|
*args, **kwargs, **formatter.attribute_dict
|
2022-02-06 19:36:37 +01:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2022-02-06 18:34:39 +01:00
|
|
|
|
2022-02-09 17:22:38 +01:00
|
|
|
class Macro(ABC):
|
|
|
|
@abstractmethod
|
2022-02-06 18:34:39 +01:00
|
|
|
def __init__(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2022-02-09 17:22:38 +01:00
|
|
|
@abstractmethod
|
2022-02-06 18:34:39 +01:00
|
|
|
def matches(self, line: str) -> bool:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2022-02-09 17:22:38 +01:00
|
|
|
@abstractmethod
|
2022-02-17 21:13:27 +01:00
|
|
|
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
|
|
|
|
"""
|
2022-02-06 18:34:39 +01:00
|
|
|
raise NotImplementedError
|
2022-02-06 19:36:37 +01:00
|
|
|
|
|
|
|
|
2022-02-09 17:22:38 +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
|
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]:
|
2022-02-09 17:22:38 +01:00
|
|
|
return line.replace(
|
|
|
|
FORMATTER_PREFIX + self.macroname,
|
|
|
|
self.macro_replacement.format(
|
|
|
|
formatter
|
2022-02-17 21:13:27 +01:00
|
|
|
)), False
|
2022-02-09 17:22:38 +01:00
|
|
|
|
|
|
|
|
2022-02-09 19:39:27 +01:00
|
|
|
class SingleLineMacro(Macro, ABC):
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
strip: str = ' %\n'
|
|
|
|
):
|
|
|
|
self.strip = strip
|
2022-02-17 20:38:25 +01:00
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
@abstractmethod
|
2022-02-18 09:50:02 +01:00
|
|
|
def _apply(self, line, formatter) -> Union[Union[str, List[str]], Tuple[Union[str, List[str]], bool]]:
|
2022-02-17 21:13:27 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
def apply(self, line: str, formatter) -> Tuple[Union[str, List[str]], bool]:
|
2022-02-18 09:50:02 +01:00
|
|
|
replacement = self._apply(line, formatter)
|
|
|
|
if isinstance(replacement, tuple):
|
|
|
|
return replacement
|
|
|
|
else:
|
|
|
|
return replacement, True
|
2022-02-17 21:13:27 +01:00
|
|
|
|
2022-02-18 00:01:16 +01:00
|
|
|
@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
|
|
|
|
|
|
|
|
|
2022-02-18 00:23:01 +01:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2022-02-18 00:01:16 +01:00
|
|
|
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
|
|
|
|
|
2022-02-17 20:38:25 +01:00
|
|
|
|
2022-02-18 00:23:01 +01:00
|
|
|
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()
|
|
|
|
|
|
|
|
|
2022-02-18 00:01:16 +01:00
|
|
|
class ConfigBeginMacro(SimpleSingleLineMacro):
|
2022-02-09 19:39:27 +01:00
|
|
|
def __init__(self):
|
|
|
|
super(ConfigBeginMacro, self).__init__(FORMATTER_PREFIX + INFILE_CONFIG_BEGIN_CONFIG)
|
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
2022-02-18 00:23:01 +01:00
|
|
|
if formatter.mode.is_drop():
|
2022-02-09 19:39:27 +01:00
|
|
|
raise NotImplementedError # invalid config begin
|
2022-02-18 00:23:01 +01:00
|
|
|
formatter.mode = formatter.mode.to_drop()
|
2022-02-09 19:39:27 +01:00
|
|
|
return []
|
|
|
|
|
2022-02-17 20:38:25 +01:00
|
|
|
|
2022-02-18 00:01:16 +01:00
|
|
|
class ConfigEndMacro(SimpleSingleLineMacro):
|
2022-02-09 19:39:27 +01:00
|
|
|
def __init__(self):
|
|
|
|
super(ConfigEndMacro, self).__init__(FORMATTER_PREFIX + INFILE_CONFIG_END_CONFIG)
|
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
2022-02-18 00:23:01 +01:00
|
|
|
if not formatter.mode.is_drop():
|
2022-02-09 19:39:27 +01:00
|
|
|
raise NotImplementedError # invalid
|
2022-02-18 00:23:01 +01:00
|
|
|
formatter.mode = formatter.mode.to_undrop()
|
2022-02-09 19:39:27 +01:00
|
|
|
return []
|
|
|
|
|
2022-02-17 20:38:25 +01:00
|
|
|
|
2022-02-18 09:50:02 +01:00
|
|
|
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'</!outtype>',
|
|
|
|
r'\end{macrocode}',
|
|
|
|
r'',
|
|
|
|
r'% \end{implementation}'
|
|
|
|
], False
|
|
|
|
|
|
|
|
|
2022-02-18 00:01:16 +01:00
|
|
|
class MacroCodeBeginMacro(SimpleSingleLineMacro):
|
2022-02-17 20:46:39 +01:00
|
|
|
def __init__(self):
|
|
|
|
super(MacroCodeBeginMacro, self).__init__(r'\begin{macrocode}')
|
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
2022-02-17 20:46:39 +01:00
|
|
|
if not formatter.mode == FormatterMode.meta:
|
2022-02-17 23:16:41 +01:00
|
|
|
raise PyTeXInvalidBeginMacroCodeUsageError(
|
|
|
|
r"\begin{macrocode} used outside meta context"
|
|
|
|
)
|
2022-02-17 20:46:39 +01:00
|
|
|
formatter.mode = FormatterMode.macrocode
|
|
|
|
return r'% \begin{macrocode}'
|
|
|
|
|
|
|
|
|
2022-02-18 00:01:16 +01:00
|
|
|
class MacroCodeEndMacro(SimpleSingleLineMacro):
|
2022-02-17 20:46:39 +01:00
|
|
|
def __init__(self):
|
|
|
|
super(MacroCodeEndMacro, self).__init__(r'\end{macrocode}')
|
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
2022-02-17 20:46:39 +01:00
|
|
|
if not formatter.mode == FormatterMode.macrocode:
|
2022-02-17 23:16:41 +01:00
|
|
|
raise PyTeXInvalidEndMacroCodeUsageError(
|
|
|
|
r"\end{macrocode} used outside macrocode context"
|
|
|
|
)
|
2022-02-17 20:46:39 +01:00
|
|
|
formatter.mode = FormatterMode.meta
|
|
|
|
return r'% \end{macrocode}'
|
|
|
|
|
|
|
|
|
2022-02-09 17:22:38 +01:00
|
|
|
class ArgumentMacro(Macro):
|
2022-02-06 19:36:37 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
2022-02-09 17:22:38 +01:00
|
|
|
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:
|
2022-02-09 17:22:38 +01:00
|
|
|
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
|
|
|
|
|
2022-02-17 21:13:27 +01:00
|
|
|
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
|
2022-02-17 21:13:27 +01:00
|
|
|
), False
|