tex2typst
Version:
JavaScript library for converting TeX code to Typst
75 lines (64 loc) • 2.77 kB
text/typescript
import { array_includes, array_split } from "./generic";
import { TexNode, TexToken, TexSupsubData, TexTokenType } from "./types";
export class TexWriter {
protected buffer: string = "";
queue: TexToken[] = [];
private writeBuffer(token: TexToken) {
const str = token.toString();
let no_need_space = false;
if (token.type === TexTokenType.SPACE) {
no_need_space = true;
} else {
// putting the first token in clause
no_need_space ||= /[{\(\[\|]$/.test(this.buffer);
// opening a optional [] parameter for a command
no_need_space ||= /\\\w+$/.test(this.buffer) && str === '[';
// putting a punctuation
no_need_space ||= /^[\.,;:!\?\(\)\]{}_^]$/.test(str);
no_need_space ||= ['\\{', '\\}'].includes(str);
// putting a prime
no_need_space ||= str === "'";
// putting a subscript or superscript
no_need_space ||= this.buffer.endsWith('_') || this.buffer.endsWith('^');
// buffer ends with a whitespace
no_need_space ||= /\s$/.test(this.buffer);
// token starts with a space
no_need_space ||= /^\s/.test(str);
// buffer is empty
no_need_space ||= this.buffer === '';
// leading sign. e.g. produce "+1" instead of " +1"
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === '-' || this.buffer === '+';
// "&=" instead of "& ="
no_need_space ||= this.buffer.endsWith('&') && str === '=';
}
if (!no_need_space) {
this.buffer += ' ';
}
this.buffer += str;
}
public append(node: TexNode) {
const alignment_char = new TexNode('control', '&');
const newline_char = new TexNode('control', '\\\\');
if (node.type === 'ordgroup' && array_includes(node.args!, alignment_char)) {
// wrap the whole math formula with \begin{aligned} and \end{aligned}
const rows = array_split(node.args!, newline_char);
const data: TexNode[][] = [];
for(const row of rows) {
const cells = array_split(row, alignment_char);
data.push(cells.map(cell => new TexNode('ordgroup', '', cell)));
}
node = new TexNode('beginend', 'aligned', [], data);
}
this.queue = this.queue.concat(node.serialize());
}
protected flushQueue() {
for (let i = 0; i < this.queue.length; i++) {
this.writeBuffer(this.queue[i]);
}
this.queue = [];
}
public finalize(): string {
this.flushQueue();
return this.buffer;
}
}