Compare commits
6 commits
6fe4ef8aaf
...
c7dd1a2e95
Author | SHA1 | Date | |
---|---|---|---|
c7dd1a2e95 | |||
50b29b1ae3 | |||
2e76687172 | |||
6de4ea2743 | |||
42a63b3163 | |||
a7963ba824 |
6 changed files with 135 additions and 52 deletions
|
@ -14,7 +14,7 @@ from .katex import KatexClient
|
|||
from .html import html
|
||||
from .tex import tex
|
||||
from .images import ImageProcessor
|
||||
from .output_generator import OutputGenerator
|
||||
from .output_generator import OutputGenerator, FormatitkoRecursiveError
|
||||
from .html_generator import HTMLGenerator
|
||||
from .transform_processor import TransformProcessor
|
||||
from .pandoc_processor import PandocProcessor
|
||||
|
@ -54,9 +54,15 @@ def main():
|
|||
doc = import_md(open(args.input_filename, "r").read())
|
||||
|
||||
if args.debug:
|
||||
OutputGenerator(sys.stdout).generate(doc)
|
||||
try:
|
||||
OutputGenerator(sys.stdout).generate(doc)
|
||||
except FormatitkoRecursiveError as e:
|
||||
e.pretty_print()
|
||||
|
||||
doc = TransformProcessor(args.input_filename).transform(doc)
|
||||
try:
|
||||
doc = TransformProcessor(args.input_filename).transform(doc)
|
||||
except FormatitkoRecursiveError as e:
|
||||
e.pretty_print()
|
||||
|
||||
# Initialize the image processor (this just keeps some basic state)
|
||||
imageProcessor = ImageProcessor(args.img_public_dir, args.img_web_path, args.img_cache_dir, *args.img_lookup_dirs)
|
||||
|
@ -65,11 +71,18 @@ def main():
|
|||
# Initialize KaTeX client (this runs the node app and connects to a unix socket)
|
||||
with KatexClient(socket=args.katex_socket) as katexClient:
|
||||
with open(args.output_html, "w") as file:
|
||||
HTMLGenerator(file, katexClient, imageProcessor).generate(doc)
|
||||
try:
|
||||
HTMLGenerator(file, katexClient, imageProcessor).generate(doc)
|
||||
except FormatitkoRecursiveError as e:
|
||||
e.pretty_print()
|
||||
|
||||
if args.output_tex is not None:
|
||||
with open(args.output_tex, "w") as file:
|
||||
UCWTexGenerator(file, imageProcessor).generate(doc)
|
||||
try:
|
||||
UCWTexGenerator(file, imageProcessor).generate(doc)
|
||||
except FormatitkoRecursiveError as e:
|
||||
e.pretty_print()
|
||||
|
||||
|
||||
if args.output_md is not None:
|
||||
with open(args.output_md, "w") as file:
|
||||
|
@ -83,7 +96,10 @@ def main():
|
|||
if args.output_tex is None:
|
||||
fd = tempfile.NamedTemporaryFile(dir=".", suffix=".tex")
|
||||
with open(fd.name, "w") as file:
|
||||
UCWTexGenerator(file, imageProcessor).generate(doc)
|
||||
try:
|
||||
UCWTexGenerator(file, imageProcessor).generate(doc)
|
||||
except FormatitkoRecursiveError as e:
|
||||
e.pretty_print()
|
||||
filename = fd.name
|
||||
else:
|
||||
filename = args.output_tex
|
||||
|
@ -93,7 +109,10 @@ def main():
|
|||
|
||||
if args.debug:
|
||||
print("-----------------------------------")
|
||||
OutputGenerator(sys.stdout).generate(doc)
|
||||
try:
|
||||
OutputGenerator(sys.stdout).generate(doc)
|
||||
except FormatitkoRecursiveError as e:
|
||||
e.pretty_print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -79,7 +79,7 @@ class KatexClient:
|
|||
if "error" in response:
|
||||
raise KatexServerError(response["error"])
|
||||
if "error" in response["results"][0]:
|
||||
raise KatexError(response["results"][0]["error"])
|
||||
raise KatexError(response["results"][0]["error"] + " in $" + tex + "$")
|
||||
else:
|
||||
return response["results"][0]["html"]
|
||||
|
||||
|
|
|
@ -7,15 +7,20 @@ from typing import Union, Callable
|
|||
|
||||
from .whitespace import NBSP
|
||||
from .elements import FQuoted
|
||||
from .context import Group, InlineGroup, BlockGroup
|
||||
from .context import Group, InlineGroup, BlockGroup, Context
|
||||
from .whitespace import Whitespace
|
||||
from .command import BlockCommand, InlineCommand, CodeCommand, Command
|
||||
from .output_generator import FormatitkoRecursiveError
|
||||
|
||||
ELCl = Union[Element, ListContainer, list[Union[Element, ListContainer]]]
|
||||
|
||||
class DoubleDocError(Exception):
|
||||
"TransformProcessor should only ever see a single Doc."
|
||||
pass
|
||||
|
||||
class NOPProcessor:
|
||||
TYPE_DICT: dict[type, Callable]
|
||||
context: Union[Context, None] = None
|
||||
|
||||
class UnknownElementError(Exception):
|
||||
f"An unknown Element has been passed to the NOPProcessor, probably because panflute introduced a new one."
|
||||
|
@ -96,23 +101,30 @@ class NOPProcessor:
|
|||
return []
|
||||
|
||||
def transform(self, e: ELCl) -> ELCl:
|
||||
if isinstance(e, list):
|
||||
return self.transform_list(e)
|
||||
elif isinstance(e, ListContainer):
|
||||
return self.transform_ListContainer(e)
|
||||
|
||||
for transformer in self.get_pretransformers():
|
||||
e = transformer(e)
|
||||
|
||||
try:
|
||||
e = self.TYPE_DICT[type(e)](e)
|
||||
except KeyError:
|
||||
raise self.UnknownElementError(type(e))
|
||||
if isinstance(e, list):
|
||||
return self.transform_list(e)
|
||||
elif isinstance(e, ListContainer):
|
||||
return self.transform_ListContainer(e)
|
||||
|
||||
for transformer in self.get_posttransformers():
|
||||
e = transformer(e)
|
||||
for transformer in self.get_pretransformers():
|
||||
e = transformer(e)
|
||||
|
||||
return e
|
||||
try:
|
||||
e = self.TYPE_DICT[type(e)](e)
|
||||
except KeyError:
|
||||
raise self.UnknownElementError(type(e))
|
||||
|
||||
for transformer in self.get_posttransformers():
|
||||
e = transformer(e)
|
||||
|
||||
return e
|
||||
except FormatitkoRecursiveError as err:
|
||||
if not isinstance(e, ListContainer):
|
||||
err.add_element(e)
|
||||
raise err
|
||||
except Exception as err:
|
||||
raise FormatitkoRecursiveError(e, self.context) from err
|
||||
|
||||
def transform_list(self, e: list[Union[Element, ListContainer]]) -> list[Union[Element, ListContainer]]:
|
||||
for i in range(len(e)):
|
||||
|
@ -293,6 +305,9 @@ class NOPProcessor:
|
|||
return e
|
||||
|
||||
def transform_Doc(self, e: Doc) -> Doc:
|
||||
if self.context is not None:
|
||||
raise DoubleDocError()
|
||||
self.context = Context(e, self.root_file_path)
|
||||
e.content = self.transform(e.content)
|
||||
return e
|
||||
|
||||
|
|
|
@ -3,18 +3,56 @@ from panflute import Cite, Code, Emph, Image, LineBreak, Link, Math, Note, Quote
|
|||
from panflute import BlockQuote, BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Figure, Header, HorizontalRule, LineBlock, LineItem, ListItem, MetaBlocks, MetaBool, MetaInlines, MetaList, MetaMap, MetaString, Null, OrderedList, Para, Plain, RawBlock, Table, TableBody, TableFoot, TableHead
|
||||
from panflute import TableRow, TableCell, Caption, Doc
|
||||
from panflute import MetaValue
|
||||
from panflute import stringify
|
||||
from typing import Union, Callable
|
||||
|
||||
from .whitespace import NBSP
|
||||
from .elements import FQuoted
|
||||
from .context import Group, InlineGroup, BlockGroup, Context
|
||||
|
||||
import re
|
||||
|
||||
import sys
|
||||
|
||||
class UnknownElementError(Exception):
|
||||
"An unknown Element has been passed to the OutputGenerator, probably because panflute introduced a new one."
|
||||
pass
|
||||
|
||||
|
||||
class FormatitkoRecursiveError(Exception):
|
||||
"A generic exception which wraps other exceptions and adds element-based traceback"
|
||||
elements: list[Union[Element, ListContainer, list[Union[Element, ListContainer]]]]
|
||||
context: Context
|
||||
|
||||
def __init__(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]], context: Context, *args):
|
||||
self.elements = [e]
|
||||
self.context = context
|
||||
super().__init__(args)
|
||||
|
||||
def add_element(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]):
|
||||
self.elements.append(e)
|
||||
|
||||
def pretty_print(self):
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
def print_filename_recursive(context: Context):
|
||||
return context.filename +\
|
||||
((" (included from " + print_filename_recursive(context.parent) + ")") if context.parent else "")
|
||||
eprint(f"Error occured in file {print_filename_recursive(self.context)} in ", end="")
|
||||
line = None
|
||||
for i in range(len(self.elements)-1, 0, -1):
|
||||
if hasattr(self.elements[i], "content") and len(self.elements[i].content) > 0 and isinstance(self.elements[i].content[0], Inline) and line is None:
|
||||
line = self.elements[i]
|
||||
eprint(type(self.elements[i]).__name__ + "[" + (str(self.elements[i-1].index) if isinstance(self.elements[i-1].index, int) else "") + "]", end=": ")
|
||||
if line:
|
||||
eprint()
|
||||
eprint('on line: "' + stringify(line).strip() + '"', end="")
|
||||
eprint()
|
||||
eprint("in element: " + str(self.elements[0]).replace("\n", "\\n"))
|
||||
sys.tracebacklimit = 0
|
||||
raise self.__cause__ from None
|
||||
|
||||
|
||||
class OutputGenerator:
|
||||
_empty_lines: int
|
||||
context: Union[Context, None]
|
||||
|
@ -101,28 +139,35 @@ class OutputGenerator:
|
|||
}
|
||||
|
||||
def generate(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]):
|
||||
if isinstance(e, Group):
|
||||
old_context = self.context
|
||||
self.context = e.context
|
||||
if isinstance(e, list):
|
||||
self.generate_list(e)
|
||||
elif isinstance(e, ListContainer):
|
||||
self.generate_ListContainer(e)
|
||||
elif isinstance(e, Inline):
|
||||
self.generate_Inline(e)
|
||||
elif isinstance(e, Block):
|
||||
self.generate_Block(e)
|
||||
elif isinstance(e, MetaValue):
|
||||
self.generate_MetaValue(e)
|
||||
elif isinstance(e, MetaList):
|
||||
self.generate_MetaList(e)
|
||||
else:
|
||||
try:
|
||||
self.TYPE_DICT_MISC[type(e)](e)
|
||||
except KeyError:
|
||||
raise UnknownElementError(type(e))
|
||||
if isinstance(e, Group):
|
||||
self.context = old_context
|
||||
try:
|
||||
if isinstance(e, Group):
|
||||
old_context = self.context
|
||||
self.context = e.context
|
||||
if isinstance(e, list):
|
||||
self.generate_list(e)
|
||||
elif isinstance(e, ListContainer):
|
||||
self.generate_ListContainer(e)
|
||||
elif isinstance(e, Inline):
|
||||
self.generate_Inline(e)
|
||||
elif isinstance(e, Block):
|
||||
self.generate_Block(e)
|
||||
elif isinstance(e, MetaValue):
|
||||
self.generate_MetaValue(e)
|
||||
elif isinstance(e, MetaList):
|
||||
self.generate_MetaList(e)
|
||||
else:
|
||||
try:
|
||||
self.TYPE_DICT_MISC[type(e)](e)
|
||||
except KeyError as err:
|
||||
raise UnknownElementError(type(e)) from err
|
||||
if isinstance(e, Group):
|
||||
self.context = old_context
|
||||
except FormatitkoRecursiveError as err:
|
||||
if not isinstance(e, ListContainer):
|
||||
err.add_element(e)
|
||||
raise err
|
||||
except Exception as err:
|
||||
raise FormatitkoRecursiveError(e, self.context) from err
|
||||
|
||||
def escape_special_chars(self, text: str) -> str:
|
||||
return text
|
||||
|
|
|
@ -20,15 +20,10 @@ from .context import Context, CommandCallable
|
|||
from .whitespace import Whitespace, bavlna
|
||||
from .command import BlockCommand, InlineCommand, CodeCommand, Command
|
||||
from .command_util import handle_command_define, parse_command
|
||||
from .nop_processor import NOPProcessor, ELCl
|
||||
|
||||
class DoubleDocError(Exception):
|
||||
"TransformProcessor should only ever see a single Doc."
|
||||
pass
|
||||
from .nop_processor import NOPProcessor, ELCl, DoubleDocError
|
||||
|
||||
class TransformProcessor(NOPProcessor):
|
||||
|
||||
context: Union[Context, None] = None
|
||||
root_file_path: str
|
||||
root_highlight_style: str = "default"
|
||||
_command_modules: list[tuple[Union[dict[str, CommandCallable], ModuleType], str]] = []
|
||||
|
|
|
@ -56,6 +56,15 @@ $$
|
|||
$$
|
||||
|
||||
|
||||
<!--There is an inline *emphasis with $math \error$*.-->
|
||||
|
||||
<!--
|
||||
```python {.run}
|
||||
print("bruh")
|
||||
raise Exception("Jsem piča")
|
||||
```
|
||||
-->
|
||||
|
||||
![This is a figure, go figure...](logo.svg){width=25%}What
|
||||
|
||||
![This is a figure, go figure...](logo.pdf){width=50%}
|
||||
|
|
Loading…
Reference in a new issue