~vladh/hare-regex-tex

9ac310cdd657ed0053de81872849248188134eaa — Vlad-Stefan Harbuz 2 months ago 1c026f2
fix various TeX issues and add titles
2 files changed, 71 insertions(+), 38 deletions(-)

M README.md
M regex-tex.ha
M README.md => README.md +1 -1
@@ 5,5 5,5 @@ Creates a LaTeX TikZ illustration from a regular expression using Hare's regular
## Usage

```sh
hare run regex-tex.ha '[mM]y expr?'
hare run regex-tex.ha 'My title' '[mM]y expr?'
```

M regex-tex.ha => regex-tex.ha +70 -37
@@ 12,7 12,7 @@ fn print_regex_tikz(re: regex::regex) void = {
	let line_size = 7;

	for (let inst .. re.insts) {
		fmt::printf("\\node[state")!;
		fmt::printf(`\node[state`)!;
		if (idx == 0) {
			fmt::printf(",initial")!;
		};


@@ 21,7 21,11 @@ fn print_regex_tikz(re: regex::regex) void = {
		};
		fmt::printf(",align=center,label=above:{{{}}}] (n{}) [rectangle", idx, idx)!;
		if (node_x > line_size) {
			fmt::printf(",below=of n{}", line_size * node_y)!;
			let upper = idx: int - line_size - 2;
			if (upper < 0) {
				upper = 0;
			};
			fmt::printf(",below=of n{}", upper)!;
			node_x = -1;
			node_y += 1;
		} else {


@@ 32,40 36,62 @@ fn print_regex_tikz(re: regex::regex) void = {
		fmt::print("] {")!;
		match (inst) {
		case let inst: regex::inst_lit =>
			fmt::printf("inst_lit\\\\\\textit{{\\textquotesingle{{{}}}\\textquotesingle}}", inst: rune)!;
			let s = strings::multireplace(
				strings::fromrunes([inst: rune]),
				(`#`, `\#`));
			fmt::printf(`lit\\\textit{{\textquotesingle{{{}}}\textquotesingle}}`, s)!;
		case let inst: regex::inst_charset =>
			let pos_str = "";
			if (!inst.is_positive) {
				pos_str = "not ";
			};
			fmt::printf("inst_charset\\\\\\textit{{{}$\\chi^{}$}}", pos_str, inst.idx)!;
			fmt::printf(`charset\\\textit{{{}$\chi^{}$}}`, pos_str, inst.idx)!;
		case let inst: regex::inst_any =>
			fmt::print("inst_any")!;
			fmt::print("any")!;
		case let inst: regex::inst_split =>
			fmt::printf("inst_split\\\\\\textit{{{}}}", inst: size)!;
			fmt::printf(`split\\\textit{{$\rightarrow${}}}`, inst: size)!;
		case let inst: regex::inst_jump =>
			fmt::printf("inst_jump\\\\\\textit{{{}}}", inst: size)!;
			fmt::printf(`jump\\\textit{{$\rightarrow${}}}`, inst: size)!;
		case let inst: regex::inst_skip =>
			fmt::print("inst_skip")!;
			fmt::print("skip")!;
		case let inst: regex::inst_match =>
			fmt::printf("inst_match\\\\\\textit{{{}}}", inst: bool)!;
			let anchored = "anchored";
			if (!inst) {
				anchored = "unanchored";
			};
			fmt::printf(`match\\\textit{{{}}}`, anchored)!;
		case let inst: regex::inst_groupstart =>
			fmt::print("inst_groupstart")!;
			fmt::print("groupstart")!;
		case let inst: regex::inst_groupend =>
			fmt::print("inst_groupend")!;
			fmt::print("groupend")!;
		case let inst: regex::inst_repeat =>
			fmt::printf("inst_repeat\\\\\\textit{{{}, {}, {}, {}}}",
				inst.id, inst.origin, inst.min, inst.max)!;
			const min = inst.min;
			const max = inst.max;
			let times = if (min is size && max is size) {
				yield if (min: size == max: size) {
					yield fmt::asprintf("{}", min);
				} else {
					yield fmt::asprintf("{}–{}", min, max);
				};
			} else if (min is void && max is void) {
				yield "?";
			} else if (min is void) {
				yield fmt::asprintf("–{}", max);
			} else if (max is void) {
				yield fmt::asprintf("{}–", min);
			};
			fmt::printf(`repeat\\\textit{{$\rightarrow${}, {} times}}`,
				inst.origin, times)!;
		};
		fmt::println("};")!;
		if (idx > 0) {
			if (node_x == -1) {
				fmt::printfln("\\draw[->] (n{}) to node[auto] {{}} +(1.5cm,0);",
				fmt::printfln(`\draw[->] (n{}) to node[auto] {{}} +(1.5cm,0);`,
					idx - 1)!;
				fmt::printfln("\\draw[<-] (n{}) to node[swap] {{}} +(-1.5cm,0);",
				fmt::printfln(`\draw[<-] (n{}) to node[swap] {{}} +(-1.5cm,0);`,
					idx)!;
			} else {
				fmt::printfln("\\path[->] (n{}) edge node {{}} (n{});",
				fmt::printfln(`\path[->] (n{}) edge node {{}} (n{});`,
					idx - 1, idx)!;
			};
		};


@@ 74,48 100,55 @@ fn print_regex_tikz(re: regex::regex) void = {
	};
};

fn print_regex_tex(expr: str) void = {
fn print_regex_tex(title: str, expr: str) void = {
	const re = regex::compile(expr)!;
	const expr_clean = strings::multireplace(expr, (`\`, "\\\\"));

	fmt::println("\\begin{figure}")!;
	fmt::println("\\begin{center}")!;
	fmt::printfln("{{\\Large\\texttt{{{}}}}}\\\\", expr)!;
	fmt::println("\\vspace{0.5cm}")!;
	fmt::println("\\begin{tikzpicture}[node distance=0.8cm,auto]")!;
	fmt::println(`\begin{figure}`)!;
	fmt::println(`\begin{center}`)!;
	fmt::printfln(`{{\Large {}}}\\`, title)!;
	fmt::println(`\vspace{0.5cm}`)!;
	fmt::printfln(`{{\Large\nolinkurl{{{}}}}}\\`, expr_clean)!;
	fmt::println(`\vspace{0.5cm}`)!;
	fmt::println(`\begin{tikzpicture}[node distance=0.8cm,auto]`)!;
	print_regex_tikz(re);
	fmt::println("\\end{tikzpicture}")!;
	fmt::println("\\end{center}")!;
	fmt::println("\\vspace{0.5cm}\n")!;
	fmt::println(`\end{tikzpicture}`)!;
	fmt::println(`\end{center}`)!;
	fmt::println(`\vspace{0.5cm}`)!;
	fmt::println(``)!;
	if (len(re.charsets) > 0) {
		fmt::println("Charsets:\n")!;
		fmt::println("\\begin{itemize}")!;
		fmt::println(`Charsets:`)!;
		fmt::println(``)!;
		fmt::println(`\begin{itemize}`)!;
	};
	let idx_charset = 0z;
	for (let charset .. re.charsets) {
		fmt::printfln("\\item $\\chi^{}$:", idx_charset)!;
		fmt::println("\\begin{itemize}")!;
		fmt::printfln(`\item $\chi^{}$:`, idx_charset)!;
		fmt::println(`\begin{itemize}`)!;
		for (let item .. charset) {
			match (item) {
			case let item: regex::charset_lit_item =>
				fmt::printfln("\\item \\textquotesingle{{{}}}\\textquotesingle", item: rune)!;
				fmt::printfln(`\item \textquotesingle{{\nolinkurl{{{}}}}}\textquotesingle`,
					item: rune)!;
			case let item: regex::charset_range_item =>
				fmt::println("\\item ({}, {})", item.0, item.1)!;
				fmt::printfln(`\item U+{} to U+{}`, item.0, item.1)!;
			case let item: regex::charset_class_item =>
				const class_name = strings::sub(item.0, 0, len(item.0) - 1);
				fmt::println("\\item {}", class_name)!;
				fmt::printfln(`\item {}`, class_name)!;
			};
		};
		fmt::println("\\end{itemize}")!;
		fmt::println(`\end{itemize}`)!;
		idx_charset += 1;
	};
	if (len(re.charsets) > 0) {
		fmt::println("\\end{itemize}")!;
		fmt::println(`\end{itemize}`)!;
	};
	fmt::println("\\end{figure}")!;
	fmt::println("\\pagebreak")!;
	fmt::println(`\end{figure}`)!;

};

export fn main() void = {
	print_regex_tex(os::args[len(os::args) - 1]);
	const title = os::args[len(os::args) - 2];
	const expr = os::args[len(os::args) - 1];
	print_regex_tex(title, expr);
};