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 .html import html
|
||||||
from .tex import tex
|
from .tex import tex
|
||||||
from .images import ImageProcessor
|
from .images import ImageProcessor
|
||||||
from .output_generator import OutputGenerator
|
from .output_generator import OutputGenerator, FormatitkoRecursiveError
|
||||||
from .html_generator import HTMLGenerator
|
from .html_generator import HTMLGenerator
|
||||||
from .transform_processor import TransformProcessor
|
from .transform_processor import TransformProcessor
|
||||||
from .pandoc_processor import PandocProcessor
|
from .pandoc_processor import PandocProcessor
|
||||||
|
@ -54,9 +54,15 @@ def main():
|
||||||
doc = import_md(open(args.input_filename, "r").read())
|
doc = import_md(open(args.input_filename, "r").read())
|
||||||
|
|
||||||
if args.debug:
|
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)
|
# 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)
|
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)
|
# Initialize KaTeX client (this runs the node app and connects to a unix socket)
|
||||||
with KatexClient(socket=args.katex_socket) as katexClient:
|
with KatexClient(socket=args.katex_socket) as katexClient:
|
||||||
with open(args.output_html, "w") as file:
|
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:
|
if args.output_tex is not None:
|
||||||
with open(args.output_tex, "w") as file:
|
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:
|
if args.output_md is not None:
|
||||||
with open(args.output_md, "w") as file:
|
with open(args.output_md, "w") as file:
|
||||||
|
@ -83,7 +96,10 @@ def main():
|
||||||
if args.output_tex is None:
|
if args.output_tex is None:
|
||||||
fd = tempfile.NamedTemporaryFile(dir=".", suffix=".tex")
|
fd = tempfile.NamedTemporaryFile(dir=".", suffix=".tex")
|
||||||
with open(fd.name, "w") as file:
|
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
|
filename = fd.name
|
||||||
else:
|
else:
|
||||||
filename = args.output_tex
|
filename = args.output_tex
|
||||||
|
@ -93,7 +109,10 @@ def main():
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
print("-----------------------------------")
|
print("-----------------------------------")
|
||||||
OutputGenerator(sys.stdout).generate(doc)
|
try:
|
||||||
|
OutputGenerator(sys.stdout).generate(doc)
|
||||||
|
except FormatitkoRecursiveError as e:
|
||||||
|
e.pretty_print()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -79,7 +79,7 @@ class KatexClient:
|
||||||
if "error" in response:
|
if "error" in response:
|
||||||
raise KatexServerError(response["error"])
|
raise KatexServerError(response["error"])
|
||||||
if "error" in response["results"][0]:
|
if "error" in response["results"][0]:
|
||||||
raise KatexError(response["results"][0]["error"])
|
raise KatexError(response["results"][0]["error"] + " in $" + tex + "$")
|
||||||
else:
|
else:
|
||||||
return response["results"][0]["html"]
|
return response["results"][0]["html"]
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,20 @@ from typing import Union, Callable
|
||||||
|
|
||||||
from .whitespace import NBSP
|
from .whitespace import NBSP
|
||||||
from .elements import FQuoted
|
from .elements import FQuoted
|
||||||
from .context import Group, InlineGroup, BlockGroup
|
from .context import Group, InlineGroup, BlockGroup, Context
|
||||||
from .whitespace import Whitespace
|
from .whitespace import Whitespace
|
||||||
from .command import BlockCommand, InlineCommand, CodeCommand, Command
|
from .command import BlockCommand, InlineCommand, CodeCommand, Command
|
||||||
|
from .output_generator import FormatitkoRecursiveError
|
||||||
|
|
||||||
ELCl = Union[Element, ListContainer, list[Union[Element, ListContainer]]]
|
ELCl = Union[Element, ListContainer, list[Union[Element, ListContainer]]]
|
||||||
|
|
||||||
|
class DoubleDocError(Exception):
|
||||||
|
"TransformProcessor should only ever see a single Doc."
|
||||||
|
pass
|
||||||
|
|
||||||
class NOPProcessor:
|
class NOPProcessor:
|
||||||
TYPE_DICT: dict[type, Callable]
|
TYPE_DICT: dict[type, Callable]
|
||||||
|
context: Union[Context, None] = None
|
||||||
|
|
||||||
class UnknownElementError(Exception):
|
class UnknownElementError(Exception):
|
||||||
f"An unknown Element has been passed to the NOPProcessor, probably because panflute introduced a new one."
|
f"An unknown Element has been passed to the NOPProcessor, probably because panflute introduced a new one."
|
||||||
|
@ -96,23 +101,30 @@ class NOPProcessor:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def transform(self, e: ELCl) -> ELCl:
|
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:
|
try:
|
||||||
e = self.TYPE_DICT[type(e)](e)
|
if isinstance(e, list):
|
||||||
except KeyError:
|
return self.transform_list(e)
|
||||||
raise self.UnknownElementError(type(e))
|
elif isinstance(e, ListContainer):
|
||||||
|
return self.transform_ListContainer(e)
|
||||||
|
|
||||||
for transformer in self.get_posttransformers():
|
for transformer in self.get_pretransformers():
|
||||||
e = transformer(e)
|
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]]:
|
def transform_list(self, e: list[Union[Element, ListContainer]]) -> list[Union[Element, ListContainer]]:
|
||||||
for i in range(len(e)):
|
for i in range(len(e)):
|
||||||
|
@ -293,6 +305,9 @@ class NOPProcessor:
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def transform_Doc(self, e: Doc) -> Doc:
|
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)
|
e.content = self.transform(e.content)
|
||||||
return e
|
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 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 TableRow, TableCell, Caption, Doc
|
||||||
from panflute import MetaValue
|
from panflute import MetaValue
|
||||||
|
from panflute import stringify
|
||||||
from typing import Union, Callable
|
from typing import Union, Callable
|
||||||
|
|
||||||
from .whitespace import NBSP
|
from .whitespace import NBSP
|
||||||
from .elements import FQuoted
|
from .elements import FQuoted
|
||||||
from .context import Group, InlineGroup, BlockGroup, Context
|
from .context import Group, InlineGroup, BlockGroup, Context
|
||||||
|
|
||||||
import re
|
|
||||||
|
import sys
|
||||||
|
|
||||||
class UnknownElementError(Exception):
|
class UnknownElementError(Exception):
|
||||||
"An unknown Element has been passed to the OutputGenerator, probably because panflute introduced a new one."
|
"An unknown Element has been passed to the OutputGenerator, probably because panflute introduced a new one."
|
||||||
pass
|
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:
|
class OutputGenerator:
|
||||||
_empty_lines: int
|
_empty_lines: int
|
||||||
context: Union[Context, None]
|
context: Union[Context, None]
|
||||||
|
@ -101,28 +139,35 @@ class OutputGenerator:
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]):
|
def generate(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]):
|
||||||
if isinstance(e, Group):
|
try:
|
||||||
old_context = self.context
|
if isinstance(e, Group):
|
||||||
self.context = e.context
|
old_context = self.context
|
||||||
if isinstance(e, list):
|
self.context = e.context
|
||||||
self.generate_list(e)
|
if isinstance(e, list):
|
||||||
elif isinstance(e, ListContainer):
|
self.generate_list(e)
|
||||||
self.generate_ListContainer(e)
|
elif isinstance(e, ListContainer):
|
||||||
elif isinstance(e, Inline):
|
self.generate_ListContainer(e)
|
||||||
self.generate_Inline(e)
|
elif isinstance(e, Inline):
|
||||||
elif isinstance(e, Block):
|
self.generate_Inline(e)
|
||||||
self.generate_Block(e)
|
elif isinstance(e, Block):
|
||||||
elif isinstance(e, MetaValue):
|
self.generate_Block(e)
|
||||||
self.generate_MetaValue(e)
|
elif isinstance(e, MetaValue):
|
||||||
elif isinstance(e, MetaList):
|
self.generate_MetaValue(e)
|
||||||
self.generate_MetaList(e)
|
elif isinstance(e, MetaList):
|
||||||
else:
|
self.generate_MetaList(e)
|
||||||
try:
|
else:
|
||||||
self.TYPE_DICT_MISC[type(e)](e)
|
try:
|
||||||
except KeyError:
|
self.TYPE_DICT_MISC[type(e)](e)
|
||||||
raise UnknownElementError(type(e))
|
except KeyError as err:
|
||||||
if isinstance(e, Group):
|
raise UnknownElementError(type(e)) from err
|
||||||
self.context = old_context
|
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:
|
def escape_special_chars(self, text: str) -> str:
|
||||||
return text
|
return text
|
||||||
|
|
|
@ -20,15 +20,10 @@ from .context import Context, CommandCallable
|
||||||
from .whitespace import Whitespace, bavlna
|
from .whitespace import Whitespace, bavlna
|
||||||
from .command import BlockCommand, InlineCommand, CodeCommand, Command
|
from .command import BlockCommand, InlineCommand, CodeCommand, Command
|
||||||
from .command_util import handle_command_define, parse_command
|
from .command_util import handle_command_define, parse_command
|
||||||
from .nop_processor import NOPProcessor, ELCl
|
from .nop_processor import NOPProcessor, ELCl, DoubleDocError
|
||||||
|
|
||||||
class DoubleDocError(Exception):
|
|
||||||
"TransformProcessor should only ever see a single Doc."
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TransformProcessor(NOPProcessor):
|
class TransformProcessor(NOPProcessor):
|
||||||
|
|
||||||
context: Union[Context, None] = None
|
|
||||||
root_file_path: str
|
root_file_path: str
|
||||||
root_highlight_style: str = "default"
|
root_highlight_style: str = "default"
|
||||||
_command_modules: list[tuple[Union[dict[str, CommandCallable], ModuleType], str]] = []
|
_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.svg){width=25%}What
|
||||||
|
|
||||||
![This is a figure, go figure...](logo.pdf){width=50%}
|
![This is a figure, go figure...](logo.pdf){width=50%}
|
||||||
|
|
Loading…
Reference in a new issue