frederica
Version:
LaTeX format to ASCII art
445 lines (441 loc) • 13.4 kB
JavaScript
/*
* Frederica
*
* Copyright (c) 2018 Yuichiro MORIGUCHI
*
* This software is released under the MIT License.
* http://opensource.org/licenses/mit-license.php
**/
var R = require("rena-js").clone(),
common = require("./common");
R.ignoreDefault(/[ \t\n]+/);
function createLaTeXParser(option) {
var opt = option ? option : {};
function generatePtnExpr() {
return function (ptnExprList, ptnExpr, ptnExprListWithoutBracket, ptnExprWithoutBracket) {
var trifuncs = [ "sinh", "cosh", "tanh", "sin", "cos", "tan", "csc", "sec", "cot" ];
var funcs = [ "arcsin", "arccos", "arctan", "log", "ln", "exp" ];
var sequencesSimple = {
"\\,": " ",
"\\ ": " ",
"\\infty": "oo",
"\\to": "->",
"\\leftarrow": "<-",
"\\Leftarrow": "<=",
"\\longleftarrow": "<--",
"\\Longleftarrow": "<==",
"\\Rightarrow": "=>",
"\\longrightarrow": "-->",
"\\Longrightarrow": "==>",
"\\leftrightarrow": "<->",
"\\Leftrightarrow": "<=>",
"\\longleftrightarrow": "<-->",
"\\Longleftrightarrow": "<==>",
"\\models": "|=",
"\\mapsto": "|->",
"\\cdots": "..."
};
var sequencesOp = {
"\\pm": "+-",
"\\mp": "-+",
"\\cdot": ".",
"\\times": "*",
"\\neq": "=/=",
"\\perp": "_|_",
"\\lt": "<",
"\\gt": ">",
"\\leq": "=<",
"\\geq": ">=",
"\\ll": ">>",
"\\lll": ">>>",
"\\gg": "<<",
"\\ggg": "<<<",
"\\vdash": "|-",
"\\dashv": "-|",
"\\sim": "~",
"\\lesssim": "~<",
"\\gtrsim": ">~",
"\\parallel": "||"
};
var matrixChar = {
"(": { leftUp: "/", leftDown: "\\" },
"[": { leftUp: "-", leftDown: "-" },
"\\{": { leftUp: "/", leftDown: "\\", bracket: true }
};
var accents = {
"hat": "^",
"check": "v",
"acute": "'",
"grave": "`",
"tilde": "~",
"bar": "-",
"vec": "->",
"dot": "."
};
var emphasises = {
"boldsymbol": { begin: "*", end: "*" },
"mathbb": { begin: "*|", end: "*" },
"mathfrak": { begin: "**", end: "*" }
};
var mathChars = [
// '\'': '^\\prime',
// '"': '^{\\prime\\prime}',
['Α', '\\Alpha'],
['α', '\\alpha'],
['Β', '\\Beta'],
['β', '\\beta'],
['Γ', '\\Gamma'],
['γ', '\\gamma'],
['Δ', '\\Delta'],
['δ', '\\delta'],
['Ε', '\\Epsilon'],
['ε', '\\epsilon'],
['Ζ', '\\Zeta'],
['ζ', '\\zeta'],
['Η', '\\Eta'],
['η', '\\eta'],
['Θ', '\\Theta'],
['θ', '\\theta'],
['Ι', '\\Iota'],
['ι', '\\iota'],
['Κ', '\\Kappa'],
['κ', '\\kappa'],
['Λ', '\\Lambda'],
['λ', '\\lambda'],
['Μ', '\\Mu'],
['μ', '\\mu'],
['Ν', '\\Nu'],
['ν', '\\nu'],
['Ξ', '\\Xi'],
['ξ', '\\xi'],
['Π', '\\Pi'],
['π', '\\pi'],
['Ρ', '\\Rho'],
['ρ', '\\rho'],
['Σ', '\\Sigma'],
['σ', '\\sigma'],
['Τ', '\\Tau'],
['τ', '\\tau'],
['Υ', '\\Upsilon'],
['υ', '\\upsilon'],
['Φ', '\\Phi'],
['φ', '\\phi'],
['Χ', '\\Chi'],
['χ', '\\chi'],
['Ψ', '\\Psi'],
['ψ', '\\psi'],
['Ω', '\\Omega'],
['ω', '\\omega'],
// ['°', '^\\circ'],
['△', '\\triangle'],
['□', '\\Box'],
['†', '\\dagger'],
['‡', '\\ddagger'],
['★', '\\star'],
['…', '\\cdots'],
['~', '\\sim'],
['\u00ac', '\\lnot'],
['\u00b1', '\\pm'],
['\u00d7', '\\times'],
['\u00f7', '\\div'],
['\u2200', '\\forall'],
['\u2202', '\\partial'],
['\u2203', '\\exists'],
['\u2205', '\\emptyset'],
['\u2207', '\\nabla'],
['\u221e', '\\infty'],
['\u2208', '\\in'],
['\u2209', '\\notin'],
['\u220b', '\\ni'],
['\u2213', '\\mp'],
['\u221d', '\\propto'],
['\u2220', '\\angle'],
['\u2225', '\\parallel'],
['\u2226', '\\nparallel'],
['\u2227', '\\land'],
['\u2228', '\\lor'],
['\u2229', '\\cap'],
['\u222a', '\\cup'],
['\u2234', '\\therefore'],
['\u2235', '\\because'],
['\u2243', '\\simeq'],
['\u2245', '\\cong'],
['\u2248', '\\approx'],
['\u2252', '\\simeq'],
['\u2260', '\\neq'],
['\u2261', '\\equiv'],
['\u2266', '\\leq'],
['\u2267', '\\geq'],
['\u226a', '\\ll'],
['\u226b', '\\gg'],
['\u2276', '\\lessgtr'],
['\u2277', '\\gtrless'],
['\u2282', '\\subset'],
['\u2283', '\\supset'],
['\u2286', '\\subseteq'],
['\u2287', '\\supseteq'],
['\u228a', '\\subsetneq'],
['\u228b', '\\supsetneq'],
['\u2295', '\\oplus'],
['\u2296', '\\ominus'],
['\u2297', '\\otimes'],
['\u22a5', '\\perp'],
['\u22bf', '\\triangle'],
['\u22da', '\\lesseqgtr'],
['\u22db', '\\gtreqless'],
['\u29bf', '\\odot']
];
function generateTrifuncs(funcs) {
var i,
ptnf = [];
for(i = 0; i < funcs.length; i++) {
(function(f) {
ptnf.push(R.or(
R.then("\\" + f).then("^").then(ptnExpr, function(x, b, a) { return { type: "func", item: f, sup: b }}),
R.then("\\" + f, function(x, b, a) { return { type: "func", item: f }})
));
})(funcs[i]);
}
return R.or.apply(null, ptnf);
}
function generateFuncs(funcs) {
var i,
ptnf = [];
for(i = 0; i < funcs.length; i++) {
(function(f) {
ptnf.push(R.then("\\" + f, function(x, b, a) { return { type: "func", item: f }}));
})(funcs[i]);
}
return R.or.apply(null, ptnf);
}
function generateSeqs(seqs, type) {
var i,
ptnf = [];
for(i in seqs) {
if(seqs.hasOwnProperty(i)) {
(function(f) {
ptnf.push(R.then(f, function(x, b, a) { return { type: type, item: seqs[f] }}));
})(i);
}
}
return R.or.apply(null, ptnf);
}
function generateAccents(accents) {
var i,
ptnf = [];
for(i in accents) {
if(accents.hasOwnProperty(i)) {
(function(f) {
ptnf.push(R.then("\\" + f).then("{").then(ptnExprList).then("}").action(function(a) {
return { type: "accent", body: a, accent: accents[f] }
}));
})(i);
}
}
return R.or.apply(null, ptnf);
}
function generateEmphasises(emphasises) {
var i,
ptnf = [];
for(i in emphasises) {
if(emphasises.hasOwnProperty(i)) {
(function(f) {
ptnf.push(R.then("\\" + f).then("{").then(ptnExprList).then("}").action(function(a) {
return { type: "emphasis", body: a, emphasis: emphasises[f] }
}));
})(i);
}
}
return R.or.apply(null, ptnf);
}
function generateMathChars(mathChars) {
var i,
ptnf = [];
for(i = 0; i < mathChars.length; i++) {
(function(f) {
ptnf.push(R.then(mathChars[f][1]).action(function(a) { return { type: "simple", item: mathChars[f][0] }; }));
})(i);
}
return R.or.apply(null, ptnf);
}
function generateParenCommand(left, right) {
return R.then(left)
.then(/[\(\[\.]|\\{/, function(x, b, a) { return x; })
.then(ptnExprList, function(x, b, a) { return { left: a, expr: b }; })
.then(right)
.then(/[\)\]\.]|\\}/, parenAction);
}
function parenAction(x, b, a) {
var items = [];
if(a.left === "\\{") {
items.push({ type: "simple", item: "{" });
} else if(a.left !== ".") {
items.push({ type: "simple", item: a.left });
}
items.push(a.expr);
if(x === "\\}") {
items.push({ type: "simple", item: "}" });
} else if(x !== ".") {
items.push({ type: "simple", item: x });
}
return {
type: "exprlist",
items: items
};
}
var ptnFrac = R.then("\\frac")
.then("{")
.then(ptnExprList)
.then("}")
.then("{")
.then(ptnExprList, function(x, b, a) { return { type: "frac", numerator: a, denominator: b }; })
.then("}");
var ptnRoot = R.attr(null)
.then("\\sqrt")
.then(R.maybe(R.then("[").then(ptnExprList).then("]")))
.then("{")
.then(ptnExprList, function(x, b, a) { return { type: "root", body: b, nth: a }; })
.then("}");
var ptnSimple = R.or(
R.then(/[a-zA-Z0-9]/, function(x, b, a) { return { type: "simple", item: x }; }),
R.then(/[\-\+]/, function(x, b, a) { return { type: "op", item: x }; }),
R.then(/[=]/, function(x, b, a) { return { type: "simple", item: x }; }));
var ptnBracket = R.then(/[\(\[]|\\{/, function(x, b, a) { return x; })
.then(ptnExprList, function(x, b, a) { return { left: a, expr: b }; })
.then(/[\)\]]|\\}/, parenAction);
var ptnBarBar = R.then("\\bar").then("{").then("\\bar").then("{").then(ptnExprList).then("}").then("}").action(function(a) {
return { type: "accent", body: a, accent: "=" }
});
var ptnMathrm = R.then("\\mathrm")
.then("{")
.then(/[^}\n]+/, function(x, b, a) { return { type: "mathrm", item: x }; })
.then("}");
var ptnBlock = R.then("{").then(ptnExprList).then("}");
var ptnSup = R.then("^").then(ptnExpr, function(x, b, a) { return { type: "sup", sup: b }; });
var ptnSub = R.then("_").then(ptnExpr, function(x, b, a) { return { type: "sub", sub: b }; });
var ptnSupSub = R.then("^")
.then(ptnExpr)
.then("_")
.then(ptnExpr, function(x, b, a) { return { type: "supsub", sup: a, sub: b }; });
var ptnSubSup = R.then("_")
.then(ptnExpr)
.then("^")
.then(ptnExpr, function(x, b, a) { return { type: "supsub", sup: b, sub: a }; });
var ptnSum = R.then("\\sum").attr({}).then(R.maybe(R.or(ptnSupSub, ptnSubSup, ptnSup, ptnSub))).action(function(a) {
return { type: "sum", sup: a.sup, sub: a.sub };
});
var ptnProd = R.then("\\prod").attr({}).then(R.maybe(R.or(ptnSupSub, ptnSubSup, ptnSup, ptnSub))).action(function(a) {
return { type: "prod", sup: a.sup, sub: a.sub };
});
var ptnInt = R.then("\\int").attr({}).then(R.maybe(R.or(ptnSupSub, ptnSubSup, ptnSup, ptnSub))).action(function(a) {
return { type: "int", sup: a.sup, sub: a.sub };
});
var ptnOint = R.then("\\oint").attr({}).then(R.maybe(R.or(ptnSupSub, ptnSubSup, ptnSup, ptnSub))).action(function(a) {
return { type: "oint", sup: a.sup, sub: a.sub };
});
var ptnLim = R.then("\\lim").attr({}).then(R.maybe(ptnSub)).action(function(a) {
return { type: "lim", sub: a.sub };
});
var ptnMatrixBody = R.then("\\begin").then("{").then("array").then("}").then("{").then(/[lcr]+/).then("}")
.then(R.delimit(
R.delimit(ptnExprList, "&", function(x, b, a) { return a.concat([b]); }, []),
"\\\\", function(x, b, a) { return a.concat([b]); }, []))
.then("\\end").then("{").then("array").then("}");
var ptnMatrix = R.then("\\left").then(/[\(\[]|\\{/, function(x, b, a) { return matrixChar[x]; })
.then(ptnMatrixBody, function(x, b, a) { return { body: b, leftUp: a.leftUp, leftDown: a.leftDown, bracket: a.bracket }; })
.then("\\right")
.then(/[\]\)\.]/, function(x, b, a) {
return { type: "matrix", body: a.body, leftUp: a.leftUp, leftDown: a.leftDown, bracket: a.bracket, right: x };
});
var ptnBinom = R.then("\\binom")
.then("{")
.then(ptnExprList)
.then("}")
.then("{")
.then(ptnExprList, function(x, b, a) { return { type: "binom", n: a, m: b }; })
.then("}");
var ptnSpace = R.or(
R.then("\\!", function(x, b, a) { return { type: "space" } })
);
var ptnUnknownCommand = R.then(function(str, index) {
var regex = /\\[a-zA-Z][a-zA-Z0-9]*/g,
match;
regex.lastIndex = 0;
if(!!(match = regex.exec(str.substr(index))) &&
match.index === 0 &&
match[0] !== "\\right" &&
match[0] !== "\\bigr" &&
match[0] !== "\\Bigr" &&
match[0] !== "\\biggr" &&
match[0] !== "\\Biggr" &&
match[0] !== "\\end") {
return {
match: match[0],
lastIndex: index + regex.lastIndex,
extra: match
};
} else {
return null;
}
}, function(x, b, a) { return { type: "simple", item: x }; });
var ptnPrintable = R.then(common.printable, function(x, b, a) { return { type: "simple", item: x }; });
var patterns = [
ptnSimple,
ptnBlock,
ptnMathrm,
ptnSupSub,
ptnSubSup,
ptnSup,
ptnSub,
ptnFrac,
ptnRoot,
ptnSum,
ptnProd,
ptnInt,
ptnOint,
ptnLim,
ptnMatrix,
generateParenCommand("\\left", "\\right"),
generateParenCommand("\\bigl", "\\bigr"),
generateParenCommand("\\Bigl", "\\Bigr"),
generateParenCommand("\\biggl", "\\biggr"),
generateParenCommand("\\Biggl", "\\Biggr"),
ptnBinom,
ptnSpace,
ptnBarBar,
generateAccents(accents),
generateEmphasises(emphasises),
generateTrifuncs(trifuncs),
generateFuncs(funcs)
];
if(opt.multibyte) {
patterns.push(generateMathChars(mathChars));
}
patterns = patterns.concat([
generateSeqs(sequencesSimple, "simple"),
generateSeqs(sequencesOp, "op"),
ptnBracket,
ptnUnknownCommand,
ptnPrintable
]);
return R.or.apply(null, patterns);
};
}
var ptnExprList = R.Yn(
function(ptnExprList, ptnExpr, ptnExprListWithoutBracket, ptnExprWithoutBracket) {
return R.then(R.attr([]).thenZeroOrMore(ptnExpr, function(x, b, a) { return a.concat(b); }))
.action(function(a) { return { type: "exprlist", items: a }; });
},
generatePtnExpr()
);
function parseTeX(input) {
var result = ptnExprList.parse(input);
if(result) {
return result.attribute;
} else {
throw new Error("TeX format syntax error or unsupported");
}
}
return parseTeX;
}
module.exports = createLaTeXParser;