20 releases
Uses new Rust 2024
| 0.1.21 | Jun 3, 2026 |
|---|---|
| 0.1.20 | Jun 3, 2026 |
| 0.1.19 | May 27, 2026 |
#2540 in Text processing
Used in oxyl
60KB
1.5K
SLoC
A LaTeX compiler written in Rust.
Status: very early stage - the lexer and a partial parser exit, but oxyl doesn't produce output yet. Run with --dump-tokens or --dump-ast to see what oxyl is making of your file.
What is this?
oxyl is a from-scratch LaTeX compiler. The goal is to be fast, produce helpful error messages, and eventually have proper package management built in rather than just being bolted on afterwards.
Goals
- Parse and compile
.texfiles to PDF (i really want to get this done) - Incremental compilation - only reprocess what changed
- Clear error messages + source locations
- An
oxyl.tomlpackage manifest instead of the TEXMF mess
Building
git clone https://github.com/benjibrown/oxyl.git
cargo build
./target/release/oxyl
Installing from crates.io
cargo install oxyl
oxyl <file.tex>
Usage
oxyl <file.tex> parse the file and report any errors
oxyl --dump-tokens <file.tex> print every lexer token with its byte span
oxyl --dump-ast <file.tex> print the parsed AST nodes
oxyl --color <when> <file.tex> auto (default), always, never
oxyl --no-color <file.tex> alias for --color=nevr
oxyl --version print the oxyl version
oxyl --help full flag list
Supress colour output/ANSI by setting NO_COLOR in the environment, or with --color never. Pass --color=always to force colour regardless of where stderr points (auto will only use colour if it is being output to a terminal).
To force colour through a pipe: pass --color=always on the command line, or set CLICOLOR_FORCE=1 in the environment (the bixense.com/clicolors convention).
All exit codes follow the usual Unix convention: 0 on success, 1 if the file fails to lex or parse (or cannot be read), 2 if oxyl was invoked correctly (unknown flag, missing or extra arguments).
Diagnostics include a 1-based line/column and an awesome caret pointing at the offending span:
error [E020]: unclosed '{'
--> main.tex:1:5
|
1 | foo {bar
| ^
What's currently parsed
- Plain text and paragraphs (blank lines)
- Commands with optional and mandatory arguments:
\sqrt[3]{27} - Brace groups
{ ... } - Inline math
$ ... $ - Display math
\[ ... \] - Environments
\begin{name} ... \end{name}(nesting works; extra args like the column spec in\begin{tabular}{cc}are kept on the AST node) &(alignment tab, used between cells intabular) and~(non-breaking space) - both survive into the AST as their own nodes.- Line comments (preserved in the AST so source-fidelity tools can access them)
Diagnostic Codes
| Code | Where | Meaning |
|---|---|---|
| E010 | lexer | lone backslash / non-ASCII character |
| E020 | parser | unclosed { |
| E021 | parser | unclosed mandatory argument |
| E022 | parser | unclosed optional argument |
| E030 | parser | unclosed $ (inline math) |
| E031 | parser | unclosed \[ (display math) |
| E032 | parser | stray \] (no matching \[) |
| E040 | parser | \begin missing environment name |
| E041 | parser | unclosed \begin{...} |
| E042 | parser | \end{b} does not match \begin{a} |
| E043 | parser | stray \end (no matching \begin) |