From 241e001023df7f670ef8569f45e6d3cf9b8d1e72 Mon Sep 17 00:00:00 2001
From: Greenscreener <honzikcernoh@gmail.com>
Date: Sat, 4 Feb 2023 18:49:24 +0100
Subject: [PATCH] Miscellaneous bugfixes, a huge TeX mess.

---
 .gitmodules    |  3 ++
 formatitko.py  |  4 +--
 formatitko.tex | 36 ++++++++++++++++++++++
 html.py        |  5 +++-
 test-import.md |  2 +-
 test.md        | 48 ++++++++++++++++++++++++++----
 tex.py         | 81 ++++++++++++++++++++++++++++++++++++++++++--------
 transform.py   |  2 +-
 ucwmac         |  1 +
 whitespace.py  |  6 ++--
 10 files changed, 163 insertions(+), 25 deletions(-)
 create mode 100644 .gitmodules
 create mode 100644 formatitko.tex
 create mode 160000 ucwmac

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..611d4fc
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "ucwmac"]
+	path = ucwmac
+	url = git://git.ucw.cz/ucwmac.git
diff --git a/formatitko.py b/formatitko.py
index 15c9fcf..e4587ea 100755
--- a/formatitko.py
+++ b/formatitko.py
@@ -22,9 +22,9 @@ print(show(doc))
 context = Context(doc, sys.argv[1])
 doc = doc.walk(transform, context)
 print("---------------------")
-#print(show(doc))
+print(show(doc))
 #print(convert_text(doc, input_format="panflute", output_format="markdown"))
 katexClient = KatexClient()
 open("output.html", "w").write("<head> <meta charset='utf-8'>    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css' integrity='sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0' crossorigin='anonymous'> </head>" + html(doc, katexClient))
-open("output.tex", "w").write(tex(doc))
+open("output.tex", "w").write("\input formatitko.tex\n" + tex(doc))
 #print(tex(doc))
diff --git a/formatitko.tex b/formatitko.tex
new file mode 100644
index 0000000..fbcf448
--- /dev/null
+++ b/formatitko.tex
@@ -0,0 +1,36 @@
+\input luatex85.sty
+\input ucwmac2.tex
+\ucwmodule{luaofs}
+\ucwmodule{link}
+\ucwmodule{verb}
+\parskip=3pt plus 2pt minus 1pt
+\parindent=0sp
+
+\def\strong#1{{%
+\def\emph##1{{\bi{}##1}}%
+\bf{}#1%
+}}
+\def\emph#1{{%
+\def\strong##1{{\bi{}##1}}%
+\it{}#1%
+}}
+
+\newcount\fncount
+\fncount=1
+\def\fnmark{\leavevmode\raise3pt\hbox{\fiverm\the\fncount}}
+\def\fn#1{\footnote\fnmark{#1}\advance\fncount by 1}
+
+\def\hA#1{{\parskip1em\settextsize{14}\bf #1}}
+\def\hB#1{{\parskip1em\settextsize{12}\bf #1}}
+\def\hC#1{{\parskip1em\settextsize{10}\bf #1}}
+\def\hr{{\vskip5pt\hrule\vskip5pt}}
+\long\def\blockquote#1{\vskip\lineskip\vskip\parskip\hbox{\vrule\hskip5pt\vbox{#1}}}
+\def\code#1{{\tt #1}}
+\let\codeblock\verbatim
+\def\figure#1{#1}
+\def\image#1{#1}
+\def\table#1{#1}
+\def\tablebody#1{#1}
+\def\tablerow#1{#1}
+\def\tablehead#1{#1}
+\def\tablecell#1{#1}
diff --git a/html.py b/html.py
index 8421d0b..3074d26 100644
--- a/html.py
+++ b/html.py
@@ -10,6 +10,9 @@ from katex import KatexClient
 
 def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") -> str:
 	
+	if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "html":
+		return ""
+
 	if isinstance(e, ListContainer):
 		return ''.join([html(child, k, indent_level, indent_str) for child in e])
 
@@ -191,7 +194,7 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t")
 			return ""
 
 	if isinstance(e, Inline):
-		return f"<{tag}{attributes}>{content_head}{html(e.content, k, 0, '')}{content_foot}</{tag}>"
+		return f'<{tag}{attributes}>{content_head}{html(e.content, k, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}</{tag}>'
 
 	out_str = ""
 	if not isinstance(e, Plain):
diff --git a/test-import.md b/test-import.md
index 678371b..c4f0a5c 100644
--- a/test-import.md
+++ b/test-import.md
@@ -3,5 +3,5 @@ appendChildren(element.content)
 ```
 
 ```python {define=opendatatask}
-println("Toto je praktická open-data úloha. V [odevzdávátku](https://ksp.mff.cuni.cz/h/odevzdavatko/) si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.")
+println("Toto je praktická open-data úloha. V [odevzdávátku](https://ksp.mff.cuni.cz/h/odevzdavatko/) si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.")
 ```
diff --git a/test.md b/test.md
index 931dcb3..f74b8c0 100644
--- a/test.md
+++ b/test.md
@@ -9,6 +9,11 @@ are_we_there_yet: False
 
 This is an *example* **yay**!
 
+This is *very **strongly** emphasised*
+
+Příliš žluťoučký kůň pěl dábelské ódy. *Příliš žluťoučký kůň pěl dábelské ódy.* **Příliš žluťoučký kůň pěl dábelské ódy.** ***Příliš žluťoučký kůň pěl dábelské ódy.***
+
+
 :::{partial=test-partial.md}
 :::
 
@@ -33,6 +38,8 @@ def bruh(no):
     wat
 ```
 
+Inline `code`
+
 ::::{if=cat}
 This should only be shown to cats the second time
 ::::
@@ -41,16 +48,25 @@ This should only be shown to cats the second time
 
 ![This is a figure, go figure...](/this/image/does/not/exist.jpg)
 
+```python {.run}
+ctx.set_metadata("language", "cs")
+```
 [!opendatatask]{}
-
+```python {.run}
+ctx.unset_metadata("language")
+```
 [This too!]{if=cat}
 
 [What]{.co}
 
 [An inline command with contents and **bold** and another [!nop]{} inside!]{c=nop}
 
-[!nop]{a=b} <!-- A special command! WOW -->
+[!nop]{a=b}<!-- A special command! WOW -->
 
+> OOO a blockquote mate init
+>
+>> Nesting??
+>> Woah
 
 A non-breakable&nbsp;space bro
 
@@ -58,7 +74,19 @@ A lot              of spaces
 
 A text with some inline math: $\sum_{i=1}^nn^2$. Plus some display math:
 
-<!--
+
+
+:::{only=tex}
+$$
+\eqalign{
+           2 x_2 + 6 x_3 &= 14 \cr
+     x_1 - 3 x_2 + 2 x_3 &= 5 \cr
+    -x_1 + 4 x_2 + \phantom{1} x_3 &= 2
+}
+$$
+:::
+
+:::{only=html}
 $$
 \def\eqalign#1{\begin{align*}#1\end{align*}}
 \eqalign{
@@ -66,16 +94,21 @@ $$
      x_1 - 3 x_2 + 2 x_3 &= 5 \cr
     -x_1 + 4 x_2 + \phantom{1} x_3 &= 2
 }
-$$-->
+$$
+:::
+
+<!-- TODO: This sucks -->
 
 ---
 
-This should be seen by all^[This is a footnote].
+This should be seen by all.^[This is a footnote]
 
 | Matematicko-fyzikální fakulta University Karlovy
 | Malostranské nám. 2/25
 | 118 00 Praha 1
 
+More footnotes.^[I am a foot]
+
 To Do:
 
 - buy eggs
@@ -88,6 +121,8 @@ To Do:
 2. Wooo
 3. no
 
+4) WOO
+
 ``` {=html}
 <figure>
     <video src="woah.mp4" autoplay></video>
@@ -102,6 +137,7 @@ To Do:
 i. bro
 ii. wym bro
 
+<!--
 +---------------------+-----------------------+
 | Location            | Temperature 1961-1990 |
 |                     | in degree Celsius     |
@@ -119,4 +155,4 @@ ii. wym bro
     123     123       123           123
       1     1          1        1
 -------     ------ ----------   -------
-
+-->
diff --git a/tex.py b/tex.py
index 26a17e9..2c9e849 100644
--- a/tex.py
+++ b/tex.py
@@ -6,12 +6,28 @@ from transform import FQuoted
 # Heavily inspired by: git://git.ucw.cz/labsconf2022.git
 def tex(e, indent_level: int=0, indent_str: str="\t") -> str:
 
-	content_foot = ""
-	content_head = ""
+	if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "tex":
+		return ""
 
 	if isinstance(e, ListContainer):
 		return ''.join([tex(child, indent_level, indent_str) for child in e])
 
+	content_foot = ""
+	content_head = ""
+
+	arguments = ""
+	open = "{"
+	close = "}"
+
+	tag = e.tag.lower()
+
+	tags = {
+		Header: "h"+chr(64 + e.level) if hasattr(e, "level") else "",
+	}
+	if type(e) in tags:
+		tag = tags[type(e)]
+
+
 	not_implemented = {
 		Citation: True,
 		Cite: True,
@@ -28,7 +44,7 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str:
 		Null: "",
 		LineBreak: f"\\\\",
 		SoftBreak: f" ",
-		HorizontalRule: "% TODO: hrule"
+		HorizontalRule: "\\hr\n\n"
 	}
 	if type(e) in simple_string:
 		return simple_string[type(e)]
@@ -39,12 +55,6 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str:
 	if isinstance(e, Para):
 		return tex(e.content, 0, "")+"\n\n"
 
-	if isinstance(e, Emph):
-		return f'{{\\it {tex(e.content, 0, "")}}}'
-
-	if isinstance(e, Strong):
-		return f'{{\\bf {tex(e.content, 0, "")}}}'
-	
 	if isinstance(e, FQuoted):
 		if e.style == "cs":
 			if e.quote_type == "SingleQuote":
@@ -64,12 +74,53 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str:
 			else:
 				return f'"{tex(e.content, 0, "")}"'
 
+	if isinstance(e, BulletList):
+		tag = "list"
+		open = ""
+		arguments = "{o}"
+		close = "\\endlist"
+
+	if isinstance(e, OrderedList):
+		tag = "list"
+		open = ""
+		styles = {
+			"DefaultStyle": "n",
+			"Decimal": "n",
+			"LowerRoman": "i",
+			"UpperRoman:": "I",
+			"LowerAlpha": "a",
+			"UpperAlpha": "A"
+		}
+		style = styles[e.style]
+		delimiters = {
+			"DefaultDelim": f"{style}.",
+			"Period": f"{style}.",
+			"OneParen": f"{style})",
+			"TwoParens": f"({style})"
+		}
+		style = delimiters[e.delimiter]
+		arguments = f"{{{style}}}"
+		close = "\\endlist"
+		# FIXME: Starting number of list
+	
+	if isinstance(e, ListItem):
+		tag = ":"
+
+	if isinstance(e, Link):
+		tag = "linkurl"
+		arguments = f'{{{e.url}}}'
+
 	if isinstance(e, Math):
 		if e.format == "DisplayMath":
 			return f'$${e.text}$$\n'
 		else:
 			return f'${e.text}$'
 
+	if isinstance(e, Note):
+		tag = "fn"
+		if len(e.content) == 1 and isinstance(e.content[0], Para):
+			return f'\\fn{{{tex(e.content[0].content, 0, "")}}}'
+
 	if isinstance(e, RawInline):
 		if e.format == "tex":
 			return e.text
@@ -85,6 +136,12 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str:
 	if isinstance(e, Span) or isinstance(e, Plain):
 		return tex(e.content, 0, "")
 
+	if isinstance(e, LineItem):
+		return tex(e.content, 0, "") + ("\\\\\n" if e.next else "\n")
+
+	if isinstance(e, LineBlock):
+		return f'{tex(e.content, indent_level+1, indent_str)}\n'
+
 	if isinstance(e, Div):
 		return f'{tex(e.content, indent_level+1, indent_str)}'
 
@@ -92,15 +149,15 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str:
 		return tex(e.content, indent_level, indent_str)+"\n\\bye"
 
 	if isinstance(e, Inline):
-		return f"({e.tag}){content_head}{tex(e.content, 0, '')}{content_foot}(/{e.tag})"
+		return f'\\{tag}{arguments}{open}{content_head}{tex(e.content, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}{close}'
 
 	out_str = ""
-	out_str = f"({e.tag}){{"
+	out_str = f"\\{tag}{arguments}{open}\n"
 	out_str += content_head
 	if hasattr(e, "_content"):
 		out_str += tex(e.content, indent_level+1, indent_str)
 	if hasattr(e, "text"):
 		out_str += e.text
-	out_str += f"{content_foot}}}\n\n"
+	out_str += f"{content_foot}\n{close}\n\n"
 
 	return out_str
diff --git a/transform.py b/transform.py
index be158d6..87dff03 100644
--- a/transform.py
+++ b/transform.py
@@ -97,7 +97,7 @@ def transform(e: Element, c: Context) -> Element: # Returns next sibling element
 			elif isinstance(val, MetaBool):
 				e = Span(Str(str(val.boolean)))
 			else:
-				raise ValueError(f"Cannot print value of metadatum {e.content[0].text[1:]}")
+				raise TypeError(f"Cannot print value of metadatum '{e.content[0].text[1:]}' of type '{type(val)}'")
 
 	## Execute commands
 	# panflute's walk transforms the children first, then the root element, so
diff --git a/ucwmac b/ucwmac
new file mode 160000
index 0000000..18104ac
--- /dev/null
+++ b/ucwmac
@@ -0,0 +1 @@
+Subproject commit 18104ac1a84b61b09564bd3d8ab0948e502c51e3
diff --git a/whitespace.py b/whitespace.py
index 5b1ecc1..28a5336 100644
--- a/whitespace.py
+++ b/whitespace.py
@@ -14,8 +14,10 @@ def bavlna(e: Whitespace, c: Context) -> bool:
 	
 	
 	if c.get_metadata("language") == "cs":
-		if isinstance(e.prev, Str) and isinstance(e.next, Str):
-			if e.prev.text.lower() in ['k', 's', 'v', 'z', 'o', 'u', 'a', 'i']:
+		prev = e.prev if isinstance(e.prev, Str) else (e.prev.content[-1] if hasattr(e.prev, "content") and len(e.prev.content) != 0 else None)
+		next = e.next if isinstance(e.next, Str) else (e.next.content[0] if hasattr(e.next, "content") and len(e.next.content) != 0 else None)
+		if isinstance(prev, Str) and isinstance(next, Str):
+			if prev.text.lower() in ['k', 's', 'v', 'z', 'o', 'u', 'a', 'i']:
 				return True
 			
 	if isinstance(e.prev, Str) and isinstance(e.next, Str):