add formatter class and derive package formatter from it. future: add class formatter

This commit is contained in:
Maximilian Keßler 2021-10-08 16:12:48 +02:00
parent dbdd99cc3d
commit 9df6bd0513
5 changed files with 152 additions and 139 deletions

View file

@ -22,8 +22,8 @@ LICENSE = [
]
PACKAGE_INFO_TEXT = [
"This LaTeX package is free software and distributed under the MIT License. You",
"may use it freely for your purposes. The latest version of the package can be",
"This LaTeX {latex_file_type} is free software and distributed under the MIT License. You",
"may use it freely for your purposes. The latest version of the {latex_file_type} can be",
"obtained via GitHub under",
" https://github.com/kesslermaximilian/LatexPackages",
"For further information see the url above.",
@ -32,10 +32,10 @@ PACKAGE_INFO_TEXT = [
]
PYTEX_INFO_TEXT = [
"This package has been generated by PyTeX, available at",
"This {latex_file_type} has been generated by PyTeX, available at",
" https://github.com/kesslermaximilian/PyTeX",
"and built from source file '{source_file}'.",
"It is STRONGLY DISCOURAGED to edit this source file directly, since local",
"changes will not be versioned by Git and overwritten by the next build. Always",
"edit the source file and build the package again."
"edit the source file and build the {latex_file_type} again."
]

View file

@ -2,11 +2,11 @@ from enum import Enum
class Attributes(Enum):
package_name_raw = 'package_name_raw'
name_raw = 'name_raw'
author = 'author'
author_acronym = 'author_acronym'
package_name = 'package_name'
package_prefix = 'package_prefix'
name_lowercase = 'name_lowercase'
prefix = 'prefix'
file_name = 'file_name'
date = 'date'
year = 'year'

101
formatter.py Normal file
View file

@ -0,0 +1,101 @@
import datetime
import re
from pathlib import Path
from typing import Dict
from datetime import *
from enums import Attributes, Args
class Formatter:
def __init__(self, name: str, author: str, extra_header: str, file_extension: str):
self.extra_header = extra_header
self.name_raw = name
self.author = author
author_parts = self.author.lower().replace('ß', 'ss').split(' ')
self.author_acronym = author_parts[0][0] + author_parts[-1]
self.name_lowercase = r'{prefix}-{name}'.format(prefix=self.author_acronym,
name=self.name_raw.lower().strip().replace(' ', '-'))
self.prefix = self.name_lowercase.replace('-', '@') + '@'
self.file_name = self.name_lowercase + file_extension
self.date = datetime.now().strftime('%Y/%m/%d')
self.year = int(datetime.now().strftime('%Y'))
self.replace_dict: Dict = {}
self.arg_replace_dict: Dict = {}
self.source_file_name = "not specified"
@staticmethod
def command_name2keyword(keyword: str):
return '__' + keyword.upper().strip().replace(' ', '_') + '__'
def parse_replacement_args(self, match_groups, *user_args, **user_kwargs):
new_args = []
for arg in user_args:
if type(arg) == Attributes:
new_args.append(getattr(self, arg.value))
elif type(arg) == Args:
new_args.append(match_groups[arg.value].strip())
elif type(arg) == str:
new_args.append(arg.strip())
else:
new_args += 'ERROR'
new_args = tuple(new_args)
new_kwargs = {}
for kw in user_kwargs:
if type(user_kwargs[kw]) == Attributes:
new_kwargs[kw] = getattr(self, user_kwargs[kw].value)
elif type(user_kwargs[kw]) == Args:
new_kwargs[kw] = match_groups[user_kwargs[kw].value].strip()
elif type(user_kwargs[kw]) == str:
new_kwargs[kw] = user_kwargs[kw]
else:
new_kwargs[kw] = 'ERROR'
return new_args, new_kwargs
def add_replacement(self, keyword: str, replacement: str, *args, **kwargs):
args, kwargs = self.parse_replacement_args([], *args, **kwargs)
self.replace_dict[self.command_name2keyword(keyword)] = replacement.format(*args, **kwargs)
def add_arg_replacement(self, num_args: int, keyword: str, replacement: str, *args, **kwargs):
self.arg_replace_dict[self.command_name2keyword(keyword)] = {
'num_args': num_args,
'replacement': replacement,
'format_args': args,
'format_kwargs': kwargs
}
def format_string(self, contents: str) -> str:
for key in self.replace_dict.keys():
contents = contents.replace(key, self.replace_dict[key])
return contents
def format_string_with_arg(self, contents: str) -> str:
for command in self.arg_replace_dict.keys():
search_regex = re.compile(r'{keyword}\({arguments}(?<!@)\)'.format(
keyword=command,
arguments=','.join(['(.*?)'] * self.arg_replace_dict[command]['num_args'])
))
match = re.search(search_regex, contents)
while match is not None:
format_args, format_kwargs = self.parse_replacement_args(
list(map(lambda group: group.replace('@)', ')'), match.groups())),
*self.arg_replace_dict[command]['format_args'],
**self.arg_replace_dict[command]['format_kwargs']
)
contents = contents.replace(
match.group(),
self.arg_replace_dict[command]['replacement'].format(*format_args, **format_kwargs)
)
match = re.search(search_regex, contents)
return contents
def format_file(self, input_path: Path, output_dir: Path = None):
self.source_file_name = str(input_path.name)
input_file = input_path.open()
lines = input_file.readlines()
newlines = []
for line in lines:
newlines += self.format_string_with_arg(self.format_string(line))
if output_dir is None:
output_dir = input_path.parent
output_dir.mkdir(parents=True, exist_ok=True)
(output_dir / self.file_name).write_text(''.join(newlines))

View file

@ -1,102 +1,12 @@
import datetime
import re
from pathlib import Path
from typing import Dict
from datetime import *
from formatter import Formatter
from config import DEFAULT_AUTHOR
from enums import Attributes, Args
from replacements import make_default_commands
class PackageFormatter:
def __init__(self, package_name: str, author: str = DEFAULT_AUTHOR, extra_header: str = ""):
self.extra_header = extra_header
self.package_name_raw = package_name
self.author = author
author_parts = self.author.lower().replace('ß', 'ss').split(' ')
self.author_acronym = author_parts[0][0] + author_parts[-1]
self.package_name = r'{prefix}-{name}'.format(prefix=self.author_acronym,
name=self.package_name_raw.lower().strip().replace(' ', '-'))
self.package_prefix = self.package_name.replace('-', '@') + '@'
self.file_name = self.package_name + '.sty'
self.date = datetime.now().strftime('%Y/%m/%d')
self.year = int(datetime.now().strftime('%Y'))
self.replace_dict: Dict = {}
self.arg_replace_dict: Dict = {}
self.source_file_name = "not specified"
class PackageFormatter(Formatter):
def __init__(self, package_name: str, author: str = DEFAULT_AUTHOR, extra_header: str = ''):
Formatter.__init__(self, package_name, author, extra_header, '.sty')
@staticmethod
def command_name2keyword(keyword: str):
return '__' + keyword.upper().strip().replace(' ', '_') + '__'
def make_default_macros(self):
make_default_commands(self, 'package')
def parse_replacement_args(self, match_groups, *user_args, **user_kwargs):
new_args = []
for arg in user_args:
if type(arg) == Attributes:
new_args.append(getattr(self, arg.value))
elif type(arg) == Args:
new_args.append(match_groups[arg.value].strip())
elif type(arg) == str:
new_args.append(arg.strip())
else:
new_args += 'ERROR'
new_args = tuple(new_args)
new_kwargs = {}
for kw in user_kwargs:
if type(user_kwargs[kw]) == Attributes:
new_kwargs[kw] = getattr(self, user_kwargs[kw].value)
elif type(user_kwargs[kw]) == Args:
new_kwargs[kw] = match_groups[user_kwargs[kw].value].strip()
elif type(user_kwargs[kw]) == str:
new_kwargs[kw] = user_kwargs[kw]
else:
new_kwargs[kw] = 'ERROR'
return new_args, new_kwargs
def add_replacement(self, keyword: str, replacement: str, *args, **kwargs):
args, kwargs = self.parse_replacement_args([], *args, **kwargs)
self.replace_dict[self.command_name2keyword(keyword)] = replacement.format(*args, **kwargs)
def add_arg_replacement(self, num_args: int, keyword: str, replacement: str, *args, **kwargs):
self.arg_replace_dict[self.command_name2keyword(keyword)] = {
'num_args': num_args,
'replacement': replacement,
'format_args': args,
'format_kwargs': kwargs
}
def format(self, contents: str) -> str:
for key in self.replace_dict.keys():
contents = contents.replace(key, self.replace_dict[key])
return contents
def format_with_arg(self, contents: str) -> str:
for command in self.arg_replace_dict.keys():
search_regex = re.compile(r'{keyword}\({arguments}(?<!@)\)'.format(
keyword=command,
arguments=','.join(['(.*?)'] * self.arg_replace_dict[command]['num_args'])
))
match = re.search(search_regex, contents)
while match is not None:
format_args, format_kwargs = self.parse_replacement_args(
list(map(lambda group: group.replace('@)', ')'), match.groups())),
*self.arg_replace_dict[command]['format_args'],
**self.arg_replace_dict[command]['format_kwargs']
)
contents = contents.replace(
match.group(),
self.arg_replace_dict[command]['replacement'].format(*format_args, **format_kwargs)
)
match = re.search(search_regex, contents)
return contents
def format_package(self, input_path: Path, output_dir: Path = None):
self.source_file_name = str(input_path.name)
input_file = input_path.open()
lines = input_file.readlines()
newlines = []
for line in lines:
newlines += self.format_with_arg(self.format(line))
if output_dir is None:
output_dir = input_path.parent
output_dir.mkdir(parents=True, exist_ok=True)
(output_dir / self.file_name).write_text(''.join(newlines))

View file

@ -1,62 +1,64 @@
from enums import Attributes, Args
from package_formatter import PackageFormatter
from formatter import Formatter
from config import LICENSE, PACKAGE_INFO_TEXT, PYTEX_INFO_TEXT
def make_default_commands(package_formatter: PackageFormatter):
def make_default_commands(formatter: Formatter, latex_file_type: str):
header = '%' * 80 + '\n' \
+ '\n'.join(map(lambda line: '% ' + line,
LICENSE + [''] + PACKAGE_INFO_TEXT + [''] + PYTEX_INFO_TEXT
+ [''] + package_formatter.extra_header)
+ [''] + formatter.extra_header)
) \
+ '\n' + '%' * 80 + '\n\n' \
+ '\\NeedsTeXFormat{{LaTeX2e}}\n' \
'\\ProvidesPackage{{{package_name}}}[{date} - {description}]\n\n'
package_formatter.add_arg_replacement(
'\\Provides{Type}{{{name_lowercase}}}[{date} - {description}]\n\n'
formatter.add_arg_replacement(
1, 'header',
header,
package_name=Attributes.package_name,
name_lowercase=Attributes.name_lowercase,
date=Attributes.date,
description=Args.one,
year=Attributes.year,
copyright_holders=Attributes.author,
source_file=Attributes.source_file_name
source_file=Attributes.source_file_name,
Type=latex_file_type.capitalize(),
latex_file_type=latex_file_type
)
package_formatter.add_replacement('package name', '{}', Attributes.package_name)
package_formatter.add_replacement('package prefix', '{}', Attributes.package_prefix)
package_formatter.add_arg_replacement(1, 'package macro', r'\{}{}', Attributes.package_prefix, Args.one)
package_formatter.add_replacement('file name', '{name}', name=Attributes.file_name)
package_formatter.add_replacement('date', '{}', Attributes.date)
package_formatter.add_replacement('author', '{}', Attributes.author)
package_formatter.add_arg_replacement(2, 'new if', r'\newif\if{prefix}{condition}\{prefix}{condition}{value}',
prefix=Attributes.package_prefix, condition=Args.one, value=Args.two)
package_formatter.add_arg_replacement(2, 'set if', r'\{prefix}{condition}{value}',
prefix=Attributes.package_prefix, condition=Args.one, value=Args.two)
package_formatter.add_arg_replacement(1, 'if', r'\if{prefix}{condition}', prefix=Attributes.package_prefix,
formatter.add_replacement('{Type} name'.format(Type=latex_file_type), '{}', Attributes.name_lowercase)
formatter.add_replacement('{Type} prefix'.format(Type=latex_file_type), '{}', Attributes.prefix)
formatter.add_arg_replacement(1, '{Type} macro'.format(Type=latex_file_type), r'\{}{}', Attributes.prefix, Args.one)
formatter.add_replacement('file name', '{name}', name=Attributes.file_name)
formatter.add_replacement('date', '{}', Attributes.date)
formatter.add_replacement('author', '{}', Attributes.author)
formatter.add_arg_replacement(2, 'new if', r'\newif\if{prefix}{condition}\{prefix}{condition}{value}',
prefix=Attributes.prefix, condition=Args.one, value=Args.two)
formatter.add_arg_replacement(2, 'set if', r'\{prefix}{condition}{value}',
prefix=Attributes.prefix, condition=Args.one, value=Args.two)
formatter.add_arg_replacement(1, 'if', r'\if{prefix}{condition}', prefix=Attributes.prefix,
condition=Args.one)
package_formatter.add_replacement('language options x',
formatter.add_replacement('language options x',
r'\newif\if{prefix}english\{prefix}englishtrue' + '\n' +
r'\DeclareOptionX{{german}}{{\{prefix}englishfalse}}' + '\n' +
r'\DeclareOptionX{{ngerman}}{{\{prefix}englishfalse}}' + '\n' +
r'\DeclareOptionX{{english}}{{\{prefix}englishtrue}}',
prefix=Attributes.package_prefix)
package_formatter.add_replacement('language options',
prefix=Attributes.prefix)
formatter.add_replacement('language options',
r'\newif\if{prefix}english\{prefix}englishtrue' + '\n' +
r'\DeclareOption{{german}}{{\{prefix}englishfalse}}' + '\n' +
r'\DeclareOption{{ngerman}}{{\{prefix}englishfalse}}' + '\n' +
r'\DeclareOption{{english}}{{\{prefix}englishtrue}}',
prefix=Attributes.package_prefix)
package_formatter.add_arg_replacement(1, 'info', r'\PackageInfo{{{name}}}{{{info}}}', name=Attributes.package_name,
info=Args.one)
package_formatter.add_arg_replacement(1, 'warning', r'\PackageWarning{{{name}}}{{{warning}}}',
name=Attributes.package_name, warning=Args.one)
package_formatter.add_arg_replacement(1, 'error', r'\PackageError{{{name}}}{{{error}}}}',
name=Attributes.package_name, error=Args.one)
package_formatter.add_replacement('end options x',
r"\DeclareOptionX*{{\PackageWarning{{{package_name}}}"
prefix=Attributes.prefix)
formatter.add_arg_replacement(1, 'info', r'\{Type}Info{{{name}}}{{{info}}}', name=Attributes.name_lowercase,
info=Args.one, Type=latex_file_type.capitalize())
formatter.add_arg_replacement(1, 'warning', r'\{Type}Warning{{{name}}}{{{warning}}}',
name=Attributes.name_lowercase, warning=Args.one, Type=latex_file_type.capitalize())
formatter.add_arg_replacement(1, 'error', r'\{Type}Error{{{name}}}{{{error}}}}',
name=Attributes.name_lowercase, error=Args.one, Type=latex_file_type.capitalize())
formatter.add_replacement('end options x',
r"\DeclareOptionX*{{\{Type}Warning{{{name_lowercase}}}"
r"{{Unknown '\CurrentOption'}}}}" + '\n' + r'\ProcessOptionsX\relax' + '\n',
package_name=Attributes.package_name)
package_formatter.add_replacement('end options',
r"\DeclareOption*{{\PackageWarning{{{package_name}}}"
name_lowercase=Attributes.name_lowercase, Type=latex_file_type.capitalize())
formatter.add_replacement('end options',
r"\DeclareOption*{{\{Type}Warning{{{name_lowercase}}}"
r"{{Unknown '\CurrentOption'}}}}" + '\n' + r'\ProcessOptions\relax' + '\n',
package_name=Attributes.package_name)
name_lowercase=Attributes.name_lowercase, Type=latex_file_type.capitalize())