pytex/PyTeX/format/tex_formatter.py

124 lines
3.6 KiB
Python

from typing import List, TextIO, Optional, Tuple, Dict
from pathlib import Path
from .pytex_formatter import PyTeXFormatter
from .macros import Macro
class LineStream:
def __init__(self, filename: Path):
self._file = filename
self._handle = open(filename, 'r')
self._cached_lines: List[str] = []
self._file_exhausted: bool = False
def current_line(self):
return self.future_line(0)
def set_line(self, line):
self.set_future_line(0, line)
def pop_line(self) -> str:
self.reserve_lines(1)
line = self._cached_lines[0]
self._cached_lines = self._cached_lines[1:]
return line
def push_line(self, line):
self.push_lines([line])
def push_lines(self, lines: List[str]):
self._cached_lines = lines + self._cached_lines
def future_line(self, pos: int):
self.reserve_lines(pos + 1)
return self._cached_lines[pos]
def set_future_line(self, line, pos):
self.reserve_lines(pos + 1)
self._cached_lines[pos] = line
def reserve_lines(self, num_lines):
for i in range(0, num_lines - len(self._cached_lines) -1):
self._cached_lines.append(
self._handle.readline()
)
if self._cached_lines[-1] == '':
self._handle.close()
self._file_exhausted = True
@property
def exhausted(self) -> bool:
return self._file_exhausted & len(self._cached_lines) == 0
class TexFormatter(PyTeXFormatter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._macros: List[Macro] = []
self._line_stream: LineStream = None
self._output_file: TextIO = None
@property
def line_stream(self) -> LineStream:
if self._line_stream is None:
self._line_stream = LineStream(self.input_file)
return self._line_stream
@property
def macros(self) -> List[Macro]:
return self._macros
def _handle_macro(self, macro: Macro):
res = macro.apply(
self.line_stream.current_line(),
self
)
if isinstance(res, str):
self.line_stream.set_line(res)
else:
self.line_stream.pop_line()
self.line_stream.push_lines(res)
def _shipout_line(self):
line = self.line_stream.pop_line().rstrip()
self.write_line(line)
def write_line(self, line: str):
self._output_file.write(line)
def open_output_stream(self):
raise NotImplementedError
@property
def future_config(self) -> Optional[List[Tuple[str, Dict]]]:
raise NotImplementedError
def format_pre_header(self) -> None:
pass
def format_header(self):
self._output_file.write(self.make_header())
def format_post_header(self) -> None:
pass
def format_document(self) -> None:
while not self.line_stream.exhausted:
recent_replacement = True
while recent_replacement:
recent_replacement = False
for macro in self.macros:
if macro.matches(self.line_stream.current_line()):
self._handle_macro(macro)
recent_replacement = True
break
self._shipout_line()
def format(self, build_dir: Path, overwrite: bool = False) -> Optional[List[Tuple[str, Dict]]]:
self.open_output_stream()
self.format_pre_header()
self.format_header()
self.format_post_header()
self.format_document()
self._output_file.close()
return self.future_config