@cortex-js/compute-engine
Version:
Symbolic computing and numeric evaluations for JavaScript and Node.js
1,700 lines (1,688 loc) • 519 kB
JavaScript
/** LatexSyntax 0.58.0 */
(function(global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.LatexSyntax = {}));})(this, (function (exports) { 'use strict';
var LatexSyntax = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/latex-syntax.ts
var latex_syntax_exports = {};
__export(latex_syntax_exports, {
ALGEBRA_DICTIONARY: () => DEFINITIONS_ALGEBRA,
ARITHMETIC_DICTIONARY: () => DEFINITIONS_ARITHMETIC,
CALCULUS_DICTIONARY: () => DEFINITIONS_CALCULUS,
COMPLEX_DICTIONARY: () => DEFINITIONS_COMPLEX,
CORE_DICTIONARY: () => DEFINITIONS_CORE,
INEQUALITIES_DICTIONARY: () => DEFINITIONS_INEQUALITIES,
LATEX_DICTIONARY: () => LATEX_DICTIONARY,
LINEAR_ALGEBRA_DICTIONARY: () => DEFINITIONS_LINEAR_ALGEBRA,
LOGIC_DICTIONARY: () => DEFINITIONS_LOGIC,
LatexSyntax: () => LatexSyntax,
OTHERS_DICTIONARY: () => DEFINITIONS_OTHERS,
PHYSICS_DICTIONARY: () => DEFINITIONS_PHYSICS,
SETS_DICTIONARY: () => DEFINITIONS_SETS,
STATISTICS_DICTIONARY: () => DEFINITIONS_STATISTICS,
SYMBOLS_DICTIONARY: () => DEFINITIONS_SYMBOLS,
TRIGONOMETRY_DICTIONARY: () => DEFINITIONS_TRIGONOMETRY,
UNITS_DICTIONARY: () => DEFINITIONS_UNITS,
parse: () => parse2,
serialize: () => serialize,
version: () => version
});
// src/math-json/utils.ts
var MISSING = ["Error", "'missing'"];
function isNumberExpression(expr) {
if (typeof expr === "number" || isNumberObject(expr)) return true;
if (typeof expr === "string" && matchesNumber(expr)) return true;
return false;
}
function isNumberObject(expr) {
return expr !== null && typeof expr === "object" && "num" in expr;
}
function isSymbolObject(expr) {
return expr !== null && typeof expr === "object" && "sym" in expr;
}
function isStringObject(expr) {
return expr !== null && typeof expr === "object" && "str" in expr;
}
function isDictionaryObject(expr) {
return expr !== null && typeof expr === "object" && "dict" in expr && typeof expr.dict === "object" && !Array.isArray(expr.dict) && expr.dict !== null;
}
function isFunctionObject(expr) {
return expr !== null && typeof expr === "object" && "fn" in expr && Array.isArray(expr.fn) && expr.fn.length > 0 && typeof expr.fn[0] === "string";
}
function stringValue(expr) {
if (expr === null || expr === void 0) return null;
if (typeof expr === "object" && "str" in expr) return expr.str;
if (typeof expr !== "string") return null;
if (expr.length >= 2 && expr.at(0) === "'" && expr.at(-1) === "'")
return expr.substring(1, expr.length - 1);
if (matchesNumber(expr) || matchesSymbol(expr)) return null;
return expr;
}
function stripText(expr) {
if (expr === null || expr === void 0 || stringValue(expr) !== null)
return null;
const h = operator(expr);
if (!h) return expr;
return [
h,
...operands(expr).map((x) => stripText(x)).filter((x) => x !== null)
];
}
function operator(expr) {
if (Array.isArray(expr)) return expr[0];
if (expr === null || expr === void 0) return "";
if (isFunctionObject(expr)) return expr.fn[0];
return "";
}
function operands(expr) {
if (Array.isArray(expr)) return expr.slice(1);
if (expr !== void 0 && isFunctionObject(expr)) return expr.fn.slice(1);
return [];
}
function operand(expr, n) {
if (Array.isArray(expr)) return expr[n] ?? null;
if (expr === null || !isFunctionObject(expr)) return null;
return expr.fn[n] ?? null;
}
function nops(expr) {
if (expr === null || expr === void 0) return 0;
if (Array.isArray(expr)) return Math.max(0, expr.length - 1);
if (isFunctionObject(expr)) return Math.max(0, expr.fn.length - 1);
return 0;
}
function unhold(expr) {
if (expr === null || expr === void 0) return null;
if (operator(expr) === "Hold") return operand(expr, 1);
return expr;
}
function symbol(expr) {
if (typeof expr === "string" && matchesSymbol(expr)) {
if (expr.length >= 2 && expr.at(0) === "`" && expr.at(-1) === "`")
return expr.slice(1, -1);
return expr;
}
if (expr === null || expr === void 0) return null;
if (isSymbolObject(expr)) return expr.sym;
return null;
}
function keyValuePair(expr) {
const h = operator(expr);
if (h === "KeyValuePair" || h === "Tuple" || h === "Pair") {
const [k, v] = operands(expr);
const key = stringValue(k);
if (!key) return null;
return [key, v ?? "Nothing"];
}
return null;
}
function dictionaryFromExpression(expr) {
if (expr === null) return null;
if (isDictionaryObject(expr)) return expr;
const kv = keyValuePair(expr);
if (kv) return { [kv[0]]: kv[1] };
if (operator(expr) === "Dictionary") {
const dict = {};
const ops = operands(expr);
for (let i = 1; i < nops(expr); i++) {
const kv2 = keyValuePair(ops[i]);
if (kv2) {
dict[kv2[0]] = expressionToDictionaryValue(kv2[1]) ?? "Nothing";
}
}
return { dict };
}
return null;
}
function dictionaryFromEntries(dict) {
const entries = Object.fromEntries(
Object.entries(dict).map(([k, v]) => [
k,
jsValueToExpression(v) ?? "Nothing"
])
);
return { dict: entries };
}
function machineValueOfString(s) {
s = s.toLowerCase().replace(/[nd]$/, "").replace(/[\u0009-\u000d\u0020\u00a0]/g, "");
if (s === "nan") return NaN;
if (/^(infinity|\+infinity|oo|\+oo)$/i.test(s)) return Infinity;
if (/^(-infinity|-oo)$/.test(s)) return -Infinity;
if (/\([0-9]+\)/.test(s)) {
const [_, body, repeat, trail] = s.match(/(.+)\(([0-9]+)\)(.*)$/) ?? [];
s = body + repeat.repeat(Math.ceil(16 / repeat.length)) + (trail ?? "");
}
return parseFloat(s);
}
function machineValue(expr) {
if (typeof expr === "number") return expr;
if (typeof expr === "string" && matchesNumber(expr))
return machineValueOfString(expr);
if (expr !== void 0 && isNumberObject(expr)) return machineValue(expr.num);
return null;
}
function rationalValue(expr) {
if (expr === void 0 || expr === null) return null;
if (symbol(expr) === "Half") return [1, 2];
const h = operator(expr);
if (!h) return null;
let numer = null;
let denom = null;
if (h === "Negate") {
const r = rationalValue(operands(expr)[0]);
if (r) return [-r[0], r[1]];
}
if (h === "Rational" || h === "Divide") {
const [n, d] = operands(expr);
numer = machineValue(n) ?? NaN;
denom = machineValue(d) ?? NaN;
}
if (h === "Power") {
const [base, exp] = operands(expr);
const exponent = machineValue(exp);
if (exponent === 1) {
numer = machineValue(base);
denom = 1;
} else if (exponent === -1) {
numer = 1;
denom = machineValue(base);
}
}
if (h === "Multiply") {
const [op1, op2] = operands(expr);
if (operator(op2) === "Power") {
const [op21, op22] = operands(op2);
if (machineValue(op22) === -1) {
numer = machineValue(op1);
denom = machineValue(op21);
}
}
}
if (numer === null || denom === null) return null;
if (Number.isInteger(numer) && Number.isInteger(denom)) return [numer, denom];
return null;
}
function mapArgs(expr, fn) {
let args = null;
if (Array.isArray(expr)) args = expr;
if (isFunctionObject(expr)) args = expr.fn;
if (args === null) return [];
let i = 1;
const result = [];
while (i < args.length) {
result.push(fn(args[i]));
i += 1;
}
return result;
}
function foldAssociativeOperator(op, lhs, rhs) {
const lhsName = operator(lhs);
const rhsName = operator(rhs);
if (lhsName === op && rhsName === op)
return [op, ...operands(lhs), ...operands(rhs)];
if (lhsName === op) return [op, ...operands(lhs), rhs];
if (rhsName === op) return [op, lhs, ...operands(rhs)];
return [op, lhs, rhs];
}
function getSequence(expr) {
if (expr === null || expr === void 0) return null;
let h = operator(expr);
if (h === "Delimiter") {
expr = operand(expr, 1);
if (expr === null) return [];
h = operator(expr);
if (h !== "Sequence") return [expr];
}
if (h !== "Sequence") return null;
return operands(expr);
}
function isEmptySequence(expr) {
if (expr === null || expr === void 0) return true;
if (expr === "Nothing") return true;
return operator(expr) === "Sequence" && nops(expr) === 0;
}
function missingIfEmpty(expr) {
return isEmptySequence(expr) ? MISSING : expr;
}
function countFunctionLeaves(xs) {
if (xs[0] === "Square") {
return countFunctionLeaves(xs.slice(1)) + 2;
}
return xs.reduce((acc, x) => acc + countLeaves(x), 0);
}
function countLeaves(expr) {
if (expr === null) return 0;
if (typeof expr === "number" || typeof expr === "string") return 1;
if (isNumberExpression(expr) || isSymbolObject(expr) || isStringObject(expr))
return 1;
if (Array.isArray(expr)) return countFunctionLeaves(expr);
if ("fn" in expr) return countFunctionLeaves(expr.fn);
const dict = dictionaryFromExpression(expr);
if (dict) {
const keys = Object.keys(dict);
return 1 + keys.length + keys.reduce((acc, x) => acc + countLeaves(dict[x]), 0);
}
return 0;
}
function matchesNumber(s) {
return /^(nan|oo|\+oo|-oo|infinity|\+infinity|-infinity)$/i.test(s) || /^[+-]?(0|[1-9][0-9]*)(\.[0-9]+)?(\([0-9]+\))?([eE][+-]?[0-9]+)?$/.test(s);
}
function matchesSymbol(s) {
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s) || s.length >= 2 && s[0] === "`" && s[s.length - 1] === "`";
}
function matchesString(s) {
if (s.length >= 2 && s[0] === "'" && s[s.length - 1] === "'") {
return true;
}
return !matchesNumber(s) && !matchesSymbol(s);
}
function jsValueToExpression(v) {
if (typeof v === "string") {
return { str: v };
} else if (typeof v === "number") {
return { num: v.toString() };
} else if (typeof v === "boolean") {
return v ? "True" : "False";
} else if (Array.isArray(v)) {
return ["List", ...v.map((x) => jsValueToExpression(x) ?? "Nothing")];
} else if (v === null) {
return null;
} else if (typeof v === "object") {
const dict = {};
for (const key in v) {
dict[key] = jsValueToExpression(v[key]) ?? "Nothing";
}
return { dict };
}
if (isFunctionObject(v) || isSymbolObject(v) || isNumberObject(v) || isStringObject(v) || isDictionaryObject(v)) {
return v;
}
return null;
}
function expressionToDictionaryValue(expr) {
if (expr === null || expr === void 0) return null;
if (isStringObject(expr)) return expr.str;
if (isNumberObject(expr)) return parseFloat(expr.num);
if (isSymbolObject(expr)) return expr.sym;
if (typeof expr === "string" || typeof expr === "number") return expr;
if (Array.isArray(expr)) return { fn: expr };
return expr;
}
// src/compute-engine/latex-syntax/types.ts
var COMPARISON_PRECEDENCE = 245;
var ASSIGNMENT_PRECEDENCE = 260;
var ARROW_PRECEDENCE = 270;
var ADDITION_PRECEDENCE = 275;
var MULTIPLICATION_PRECEDENCE = 390;
var DIVISION_PRECEDENCE = 600;
var INVISIBLE_OP_PRECEDENCE = 650;
var EXPONENTIATION_PRECEDENCE = 700;
var POSTFIX_PRECEDENCE = 810;
function isExpressionEntry(entry) {
return !("kind" in entry) || entry.kind === "expression";
}
function isSymbolEntry(entry) {
return "kind" in entry && entry.kind === "symbol";
}
function isMatchfixEntry(entry) {
return "kind" in entry && entry.kind === "matchfix";
}
function isInfixEntry(entry) {
return "kind" in entry && entry.kind === "infix";
}
function isPrefixEntry(entry) {
return "kind" in entry && entry.kind === "prefix";
}
function isPostfixEntry(entry) {
return "kind" in entry && entry.kind === "postfix";
}
function isEnvironmentEntry(entry) {
return "kind" in entry && entry.kind === "environment";
}
// src/common/grapheme-splitter.ts
function stringToCodepoints(string) {
const result = [];
for (let i = 0; i < string.length; i++) {
let code = string.charCodeAt(i);
if (code >= 55296 && code <= 56319) {
const nextCode = string.charCodeAt(i + 1);
if (nextCode >= 56320 && nextCode <= 57343) {
const lead = code - 55296;
const trail = nextCode - 56320;
code = 2 ** 16 + lead * 2 ** 10 + trail;
i++;
}
}
result.push(code);
}
return result;
}
var ZWJ = 8205;
var REGIONAL_INDICATOR = [127462, 127487];
function isEmojiCombinator(code) {
if (code === ZWJ) return true;
if (code === 65038 || code === 65039) return true;
if (code >= 127995 && code <= 127995 + 5) return true;
if (code >= 129456 && code <= 129456 + 4) return true;
if (code >= 917536 && code <= 917536 + 96) return true;
return false;
}
function isRegionalIndicator(code) {
return code >= REGIONAL_INDICATOR[0] && code <= REGIONAL_INDICATOR[1];
}
function splitGraphemes(string) {
if (/^[\u0020-\u00FF]*$/.test(string)) return string;
const result = [];
const codePoints = stringToCodepoints(string);
let index = 0;
while (index < codePoints.length) {
const code = codePoints[index++];
const next = codePoints[index];
if (next === ZWJ) {
const baseIndex = index - 1;
index += 2;
while (codePoints[index] === ZWJ) {
index += 2;
}
result.push(
String.fromCodePoint(
...codePoints.slice(baseIndex, 2 * index - baseIndex + 1)
)
);
} else if (isEmojiCombinator(next)) {
const baseIndex = index - 1;
while (isEmojiCombinator(codePoints[index])) {
index += codePoints[index] === ZWJ ? 2 : 1;
}
result.push(
String.fromCodePoint(
...codePoints.slice(baseIndex, 2 * index - baseIndex - 1)
)
);
} else if (isRegionalIndicator(code)) {
index += 1;
result.push(String.fromCodePoint(...codePoints.slice(index - 2, 2)));
} else {
result.push(String.fromCodePoint(code));
}
}
return result;
}
// src/compute-engine/latex-syntax/tokenizer.ts
var UNICODE_SUPERSCRIPT_MAP = {
"\u2070": "0",
// ⁰
"\xB9": "1",
// ¹
"\xB2": "2",
// ²
"\xB3": "3",
// ³
"\u2074": "4",
// ⁴
"\u2075": "5",
// ⁵
"\u2076": "6",
// ⁶
"\u2077": "7",
// ⁷
"\u2078": "8",
// ⁸
"\u2079": "9",
// ⁹
"\u207B": "-",
// ⁻
"\u2071": "i",
// ⁱ
"\u207F": "n"
// ⁿ
};
var UNICODE_SUBSCRIPT_MAP = {
"\u2080": "0",
// ₀
"\u2081": "1",
// ₁
"\u2082": "2",
// ₂
"\u2083": "3",
// ₃
"\u2084": "4",
// ₄
"\u2085": "5",
// ₅
"\u2086": "6",
// ₆
"\u2087": "7",
// ₇
"\u2088": "8",
// ₈
"\u2089": "9",
// ₉
"\u208B": "-"
// ₋
};
var Tokenizer = class {
s;
pos;
obeyspaces = false;
constructor(s) {
s = s.replace(/[\u200E\u200F\u2066-\u2069\u202A-\u202E]/g, "");
s = s.replace(/\u2212/g, "-");
s = s.replace(/[⁰¹²³⁴⁵⁶⁷⁸⁹⁻ⁱⁿ]+/g, (m) => {
const digits = Array.from(m).map((c) => UNICODE_SUPERSCRIPT_MAP[c]).join("");
return `^{${digits}}`;
});
s = s.replace(/[₀₁₂₃₄₅₆₇₈₉₋]+/g, (m) => {
const digits = Array.from(m).map((c) => UNICODE_SUBSCRIPT_MAP[c]).join("");
return `_{${digits}}`;
});
this.s = splitGraphemes(s);
this.pos = 0;
}
/**
* @return True if we reached the end of the stream
*/
end() {
return this.pos >= this.s.length;
}
/**
* Return the next char and advance
*/
get() {
return this.pos < this.s.length ? this.s[this.pos++] : "";
}
/**
* Return the next char, but do not advance
*/
peek() {
return this.s[this.pos];
}
/**
* Return the next substring matching regEx and advance.
*/
match(regEx) {
let execResult;
if (typeof this.s === "string") {
execResult = regEx.exec(this.s.slice(this.pos));
} else {
execResult = regEx.exec(this.s.slice(this.pos).join(""));
}
if (execResult?.[0]) {
this.pos += execResult[0].length;
return execResult[0];
}
return null;
}
/**
* Return the next token, or null.
*/
next() {
if (this.end()) return null;
if (!this.obeyspaces && this.match(/^[ \f\n\r\t\v\xA0\u2028\u2029]+/)) {
return "<space>";
} else if (this.obeyspaces && this.match(/^[ \f\n\r\t\v\xA0\u2028\u2029]/)) {
return "<space>";
}
const next = this.get();
if (next === "\\") {
if (!this.end()) {
let command = this.match(/^[a-zA-Z]+/);
if (command) {
this.match(/^[ \f\n\r\t\v\xA0\u2028\u2029]*/);
} else {
command = this.get();
if (command === " ") {
return "<space>";
}
}
return "\\" + command;
}
} else if (next === "{") {
return "<{>";
} else if (next === "}") {
return "<}>";
} else if (next === "^") {
if (this.peek() === "^") {
this.get();
const hex = this.match(
/^(\^(\^(\^(\^[0-9a-f])?[0-9a-f])?[0-9a-f])?[0-9a-f])?[0-9a-f][0-9a-f]/
);
if (hex) {
return String.fromCodePoint(
parseInt(hex.slice(hex.lastIndexOf("^") + 1), 16)
);
}
}
return next;
} else if (next === "#") {
if (!this.end()) {
let isParam = false;
if (/[0-9?]/.test(this.peek())) {
isParam = true;
if (this.pos + 1 < this.s.length) {
const after = this.s[this.pos + 1];
isParam = /[^0-9A-Za-z]/.test(after);
}
}
if (isParam) {
return "#" + this.get();
}
return "#";
}
} else if (next === "$") {
if (this.peek() === "$") {
this.get();
return "<$$>";
}
return "<$>";
}
return next;
}
};
function expand(lex, args) {
let token = lex.next();
if (!token) return [];
let result = [];
if (token === "\\relax") {
} else if (token === "\\noexpand") {
token = lex.next();
if (token) {
result.push(token);
}
} else if (token === "\\obeyspaces") {
lex.obeyspaces = true;
} else if (token === "\\space" || token === "~") {
result.push("<space>");
} else if (token === "\\bgroup") {
result.push("<{>");
} else if (token === "\\egroup") {
result.push("<}>");
} else if (token === "\\string") {
token = lex.next();
if (token) {
if (token[0] === "\\") {
Array.from(token).forEach(
(x) => result.push(x === "\\" ? "\\backslash" : x)
);
} else if (token === "<{>") {
result.push("\\{");
} else if (token === "<space>") {
result.push("~");
} else if (token === "<}>") {
result.push("\\}");
}
}
} else if (token === "\\csname") {
while (lex.peek() === "<space>") {
lex.next();
}
let command = "";
let done = false;
let tokens = [];
do {
if (tokens.length === 0) {
if (/^#[0-9?]$/.test(lex.peek())) {
const param = lex.get().slice(1);
tokens = tokenize(
args?.[param] ?? args?.["?"] ?? "\\placeholder{}",
args
);
token = tokens[0];
} else {
token = lex.next();
tokens = token ? [token] : [];
}
}
done = tokens.length === 0;
if (!done && token === "\\endcsname") {
done = true;
tokens.shift();
}
if (!done) {
done = token === "<$>" || token === "<$$>" || token === "<{>" || token === "<}>" || !!token && token.length > 1 && token[0] === "\\";
}
if (!done) {
command += tokens.shift();
}
} while (!done);
if (command) {
result.push("\\" + command);
}
result = result.concat(tokens);
} else if (token === "\\endcsname") {
} else if (token.length > 1 && token[0] === "#") {
const param = token.slice(1);
result = result.concat(
tokenize(args?.[param] ?? args?.["?"] ?? "\\placeholder{}", args)
);
} else {
result.push(token);
}
return result;
}
function tokenize(s, args = []) {
const lines = s.toString().split(/\r?\n/);
let stream = "";
let sep = "";
for (const line of lines) {
stream += sep;
sep = " ";
const m = line.match(/((?:\\%)|[^%])*/);
if (m !== null) stream += m[0];
}
const tokenizer = new Tokenizer(stream);
const result = [];
do
result.push(...expand(tokenizer, args));
while (!tokenizer.end());
return result;
}
function countTokens(s) {
return tokenize(s).length;
}
function joinLatex(segments) {
let sep = "";
let result = "";
for (const segment of segments) {
if (segment === void 0 || segment === null) continue;
if (typeof segment === "string") {
if (/[a-zA-Z]/.test(segment[0])) result += sep;
if (/\\[a-zA-Z]+\*?$/.test(segment)) sep = " ";
else sep = "";
}
result += segment.toString();
}
return result;
}
function supsub(c, body, x) {
if (body.includes(c)) body = `{${body}}`;
if (/^[0-9]$/.test(x)) return `${body}${c}${x}`;
return `${body}${c}{${x}}`;
}
function tokensToString(tokens) {
let flat = [];
if (Array.isArray(tokens)) {
for (const item of tokens) {
if (Array.isArray(item)) {
flat = [...flat, ...item];
} else {
flat.push(item);
}
}
} else {
flat = [tokens];
}
const result = joinLatex(
flat.map((token) => {
return {
"<space>": " ",
"<$$>": "$$",
"<$>": "$",
"<{>": "{",
"<}>": "}"
}[token] ?? token;
})
);
return result;
}
// src/compute-engine/latex-syntax/dictionary/definitions-relational-operators.ts
var DEFINITIONS_INEQUALITIES = [
{
latexTrigger: ["\\not", "<"],
kind: "infix",
associativity: "any",
precedence: 246,
parse: "NotLess"
},
{
name: "NotLess",
latexTrigger: ["\\nless"],
kind: "infix",
associativity: "any",
precedence: 246
},
{
latexTrigger: ["<"],
kind: "infix",
associativity: "any",
precedence: 245,
parse: "Less"
},
{
name: "Less",
latexTrigger: ["\\lt"],
kind: "infix",
associativity: "any",
precedence: 245
},
{
latexTrigger: ["<", "="],
kind: "infix",
associativity: "any",
precedence: 241,
parse: "LessEqual"
},
{
name: "LessEqual",
latexTrigger: ["\\le"],
kind: "infix",
associativity: "any",
precedence: 241
},
{
latexTrigger: ["\\leq"],
kind: "infix",
associativity: "any",
precedence: 241,
parse: "LessEqual"
},
{
latexTrigger: ["\\leqslant"],
kind: "infix",
associativity: "any",
precedence: COMPARISON_PRECEDENCE + 5,
// Note different precedence than `<=` as per MathML
parse: "LessEqual"
},
{
name: "LessNotEqual",
latexTrigger: ["\\lneqq"],
kind: "infix",
associativity: "any",
precedence: COMPARISON_PRECEDENCE
},
{
name: "NotLessNotEqual",
latexTrigger: ["\\nleqq"],
kind: "infix",
associativity: "any",
precedence: COMPARISON_PRECEDENCE
},
{
name: "LessOverEqual",
latexTrigger: ["\\leqq"],
kind: "infix",
associativity: "any",
precedence: COMPARISON_PRECEDENCE + 5
},
{
name: "GreaterOverEqual",
latexTrigger: ["\\geqq"],
kind: "infix",
associativity: "any",
precedence: COMPARISON_PRECEDENCE + 5,
parse: "GreaterEqual"
},
{
name: "Equal",
latexTrigger: ["="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
latexTrigger: ["*", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE,
parse: "StarEqual"
},
{
name: "StarEqual",
latexTrigger: ["\\star", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "PlusEqual",
latexTrigger: ["+", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "MinusEqual",
latexTrigger: ["-", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "SlashEqual",
latexTrigger: ["/", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "EqualEqual",
latexTrigger: ["=", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "EqualEqualEqual",
latexTrigger: ["=", "=", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5
},
{
name: "TildeFullEqual",
// MathML: approximately equal to
latexTrigger: ["\\cong"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "NotTildeFullEqual",
// MathML: approximately but not actually equal to
latexTrigger: ["\\ncong"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "Approx",
// Note: Mathematica TildeTilde
latexTrigger: ["\\approx"],
kind: "infix",
associativity: "right",
precedence: 247
},
{
name: "NotApprox",
// Note: Mathematica TildeTilde
latexTrigger: ["\\not", "\\approx"],
kind: "infix",
associativity: "right",
precedence: 247
},
{
name: "ApproxEqual",
// Note: Mathematica TildeEqual, MathML: `asymptotically equal to`
latexTrigger: ["\\approxeq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "NotApproxEqual",
// Note: Mathematica NotTildeEqual
latexTrigger: ["\\not", "\\approxeq"],
kind: "infix",
// Note: no LaTeX symbol for char U+2249
associativity: "right",
precedence: 250
},
{
name: "NotEqual",
latexTrigger: ["\\ne"],
kind: "infix",
associativity: "right",
precedence: 255
},
{
name: "Unequal",
latexTrigger: ["!", "="],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
// Note different precedence than \\ne per MathML
},
{
name: "GreaterEqual",
latexTrigger: ["\\ge"],
kind: "infix",
associativity: "right",
precedence: 242
// Note: different precedence than `>=` as per MathML
},
{
latexTrigger: ["\\geq"],
kind: "infix",
associativity: "right",
precedence: 242,
// Note: different precedence than `>=` as per MathML
parse: "GreaterEqual"
},
{
latexTrigger: [">", "="],
kind: "infix",
associativity: "right",
precedence: 243,
parse: "GreaterEqual"
},
{
latexTrigger: ["\\geqslant"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5,
// Note: different precedence than `>=` as per MathML
parse: "GreaterEqual"
},
{
name: "GreaterNotEqual",
latexTrigger: ["\\gneqq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "NotGreaterNotEqual",
latexTrigger: ["\\ngeqq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
latexTrigger: [">"],
kind: "infix",
associativity: "right",
precedence: 245,
parse: "Greater"
},
{
name: "Greater",
latexTrigger: ["\\gt"],
kind: "infix",
associativity: "right",
precedence: 245
},
{
name: "NotGreater",
latexTrigger: ["\\ngtr"],
kind: "infix",
associativity: "right",
precedence: 244
},
{
latexTrigger: ["\\not", ">"],
kind: "infix",
associativity: "right",
precedence: 244,
parse: "NotGreater"
},
{
name: "RingEqual",
latexTrigger: ["\\circeq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "TriangleEqual",
// MathML: delta equal to
latexTrigger: ["\\triangleq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "DotEqual",
// MathML: approaches the limit
latexTrigger: ["\\doteq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5
},
{
name: "DotEqualDot",
// MathML: Geometrically equal
latexTrigger: ["\\doteqdot"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5
},
{
name: "FallingDotEqual",
// MathML: approximately equal to or the image of
latexTrigger: ["\\fallingdotseq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5
},
{
name: "RisingDotEqual",
// MathML: image of or approximately equal to
latexTrigger: ["\\fallingdotseq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5
},
{
name: "QuestionEqual",
latexTrigger: ["\\questeq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "MuchLess",
latexTrigger: ["\\ll"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "MuchGreater",
latexTrigger: ["\\gg"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "Precedes",
latexTrigger: ["\\prec"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "Succeeds",
latexTrigger: ["\\succ"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "PrecedesEqual",
latexTrigger: ["\\preccurlyeq"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "SucceedsEqual",
latexTrigger: ["\\curlyeqprec"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "NotPrecedes",
latexTrigger: ["\\nprec"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "NotSucceeds",
latexTrigger: ["\\nsucc"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE
},
{
name: "Between",
latexTrigger: ["\\between"],
kind: "infix",
associativity: "right",
precedence: COMPARISON_PRECEDENCE + 5
}
];
// src/compute-engine/latex-syntax/utils.ts
function isInequalityOperator(operator2) {
if (typeof operator2 !== "string") return false;
return ["Less", "LessEqual", "Greater", "GreaterEqual"].includes(operator2);
}
function isEquationOperator(operator2) {
if (typeof operator2 !== "string") return false;
return ["Equal", "NotEqual"].includes(operator2);
}
// src/common/type/primitive.ts
var NUMERIC_TYPES = [
"number",
"finite_number",
"complex",
"finite_complex",
"imaginary",
"real",
"finite_real",
"rational",
"finite_rational",
"integer",
"finite_integer",
"non_finite_number"
];
var INDEXED_COLLECTION_TYPES = [
"indexed_collection",
"list",
"tuple"
];
var COLLECTION_TYPES = [
...INDEXED_COLLECTION_TYPES,
"collection",
"set",
"record",
"dictionary"
];
var SCALAR_TYPES = [
"scalar",
...NUMERIC_TYPES,
"boolean",
"string"
];
var VALUE_TYPES = [
"value",
"color",
...COLLECTION_TYPES,
...SCALAR_TYPES
];
var EXPRESSION_TYPES = [
"expression",
"symbol",
"function",
...VALUE_TYPES
];
var PRIMITIVE_TYPES = [
"any",
"unknown",
"nothing",
"never",
"error",
...EXPRESSION_TYPES
];
function isValidType(t) {
if (typeof t === "string")
return PRIMITIVE_TYPES.includes(t);
if (typeof t !== "object") return false;
if (!("kind" in t)) return false;
return t.kind === "signature" || t.kind === "union" || t.kind === "intersection" || t.kind === "negation" || t.kind === "tuple" || t.kind === "list" || t.kind === "record" || t.kind === "dictionary" || t.kind === "set" || t.kind === "function" || t.kind === "collection" || t.kind === "indexed_collection" || t.kind === "reference";
}
// src/common/type/serialize.ts
var NEGATION_PRECEDENCE = 3;
var UNION_PRECEDENCE = 1;
var INTERSECTION_PRECEDENCE = 2;
var LIST_PRECEDENCE = 4;
var RECORD_PRECEDENCE = 5;
var DICTIONARY_PRECEDENCE = 6;
var SET_PRECEDENCE = 7;
var COLLECTION_PRECEDENCE = 8;
var TUPLE_PRECEDENCE = 9;
var SIGNATURE_PRECEDENCE = 10;
var VALUE_PRECEDENCE = 11;
function typeToString(type, precedence = 0) {
if (typeof type === "string") return type;
let result = "";
switch (type.kind) {
case "value":
if (typeof type.value === "string") result = `"${type.value}"`;
else if (typeof type.value === "boolean")
result = type.value ? "true" : "false";
else result = type.value.toString();
break;
case "reference":
result = type.name;
break;
case "negation":
result = `!${typeToString(type.type, NEGATION_PRECEDENCE)}`;
break;
case "union":
result = type.types.map((t) => typeToString(t, UNION_PRECEDENCE)).join(" | ");
break;
case "intersection":
result = type.types.map((t) => typeToString(t, INTERSECTION_PRECEDENCE)).join(" & ");
break;
case "expression":
result = `expression<${symbolName(type.operator)}>`;
break;
case "symbol":
result = `symbol<${symbolName(type.name)}>`;
break;
case "numeric":
if (Number.isFinite(type.lower) && Number.isFinite(type.upper)) {
result = `${type.type}<${type.lower}..${type.upper}>`;
} else if (Number.isFinite(type.lower)) {
result = `${type.type}<${type.lower}..>`;
} else if (Number.isFinite(type.upper)) {
result = `${type.type}<..${type.upper}>`;
} else {
result = `${type.type}`;
}
break;
case "list":
if (type.dimensions && typeof type.elements === "string" && NUMERIC_TYPES.includes(type.elements)) {
if (type.dimensions === void 0) {
if (type.elements === "number") result = "tensor";
} else if (type.dimensions.length === 1) {
if (type.elements === "number") {
if (type.dimensions[0] < 0) result = "vector";
else result = `vector<${type.dimensions[0]}>`;
} else {
if (type.dimensions[0] < 0)
result = `vector<${typeToString(type.elements)}>`;
else
result = `vector<${typeToString(type.elements)}^${type.dimensions[0]}>`;
}
} else if (type.dimensions.length === 2) {
const dims = type.dimensions;
if (type.elements === "number") {
if (dims[0] < 0 && dims[1] < 0) result = "matrix";
else result = `matrix<${dims[0]}x${dims[1]}>`;
} else {
if (dims[0] < 0 && dims[1] < 0)
result = `matrix<${typeToString(type.elements)}>`;
else
result = `matrix<${typeToString(type.elements)}^(${dims[0]}x${dims[1]})>`;
}
}
}
if (!result) {
const dimensions = type.dimensions ? type.dimensions.length === 1 ? `^${type.dimensions[0].toString()}` : `^(${type.dimensions.join("x")})` : "";
result = `list<${typeToString(type.elements)}${dimensions}>`;
}
break;
case "record":
const elements = Object.entries(type.elements).map(([key, value]) => `${key}: ${typeToString(value)}`).join(", ");
result = `record<${elements}>`;
break;
case "dictionary":
result = `dictionary<${typeToString(type.values)}>`;
break;
case "set":
result = `set<${typeToString(type.elements)}>`;
break;
case "collection":
result = `collection<${typeToString(type.elements)}>`;
break;
case "indexed_collection":
result = `indexed_collection<${typeToString(type.elements)}>`;
break;
case "tuple":
if (type.elements.length === 0) result = "tuple";
else if (type.elements.length === 1) {
const [el] = type.elements;
result = `tuple<${namedElement(el)}>`;
} else {
result = "tuple<" + type.elements.map((el) => namedElement(el)).join(", ") + ">";
}
break;
case "signature":
const args = type.args ? type.args.map((arg) => namedElement(arg)).join(", ") : "";
const optArgs = type.optArgs ? type.optArgs.map((arg) => namedElement(arg) + "?").join(", ") : "";
const varArg = type.variadicArg ? type.variadicMin === 0 ? `${namedElement(type.variadicArg)}*` : `${namedElement(type.variadicArg)}+` : "";
const argsList = [args, optArgs, varArg].filter((s) => s).join(", ");
result = `(${argsList}) -> ${typeToString(type.result)}`;
break;
default:
result = "error";
}
if (precedence > 0 && precedence > getPrecedence(type.kind))
return `(${result})`;
return result;
}
function namedElement(el) {
if (el.name) return `${el.name}: ${typeToString(el.type)}`;
return typeToString(el.type);
}
function symbolName(name) {
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) return name;
return `\`${name}\``;
}
function getPrecedence(kind) {
switch (kind) {
case "negation":
return NEGATION_PRECEDENCE;
case "union":
return UNION_PRECEDENCE;
case "intersection":
return INTERSECTION_PRECEDENCE;
case "list":
return LIST_PRECEDENCE;
case "record":
return RECORD_PRECEDENCE;
case "dictionary":
return DICTIONARY_PRECEDENCE;
case "set":
return SET_PRECEDENCE;
case "collection":
case "indexed_collection":
return COLLECTION_PRECEDENCE;
case "tuple":
return TUPLE_PRECEDENCE;
case "signature":
return SIGNATURE_PRECEDENCE;
case "value":
return VALUE_PRECEDENCE;
default:
return 0;
}
}
// src/common/type/lexer.ts
var Lexer = class {
input;
pos = 0;
line = 1;
column = 1;
tokens = [];
constructor(input) {
this.input = input;
}
// Save current lexer state for backtracking
saveState() {
return {
pos: this.pos,
line: this.line,
column: this.column,
tokens: [...this.tokens]
};
}
// Restore lexer state for backtracking
restoreState(state) {
this.pos = state.pos;
this.line = state.line;
this.column = state.column;
this.tokens = state.tokens;
}
error(message) {
throw new Error(
`Lexer error at line ${this.line}, column ${this.column}: ${message}`
);
}
peek(offset = 0) {
const index = this.pos + offset;
return index < this.input.length ? this.input[index] : "";
}
advance() {
const char = this.input[this.pos++];
if (char === "\n") {
this.line++;
this.column = 1;
} else {
this.column++;
}
return char;
}
match(str) {
if (this.input.slice(this.pos, this.pos + str.length) === str) {
for (let i = 0; i < str.length; i++) {
this.advance();
}
return true;
}
return false;
}
isEOF() {
return this.pos >= this.input.length;
}
skipWhitespace() {
while (!this.isEOF() && /\s/.test(this.peek())) {
this.advance();
}
}
readIdentifier() {
let value = "";
while (!this.isEOF() && /[a-zA-Z0-9_]/.test(this.peek())) {
value += this.advance();
}
return value;
}
readVerbatimString() {
if (!this.match("`")) return "";
let value = "";
while (!this.isEOF() && this.peek() !== "`") {
if (this.match("\\`")) {
value += "`";
} else if (this.match("\\\\")) {
value += "\\";
} else {
value += this.advance();
}
}
if (this.isEOF()) {
this.error("Unterminated verbatim string");
}
this.advance();
return value;
}
readStringLiteral() {
const quote = this.advance();
let value = "";
while (!this.isEOF() && this.peek() !== quote) {
if (this.match("\\" + quote)) {
value += quote;
} else if (this.match("\\\\")) {
value += "\\";
} else {
value += this.advance();
}
}
if (this.isEOF()) {
this.error("Unterminated string literal");
}
this.advance();
return value;
}
readNumber() {
let value = "";
if (this.peek() === "-" || this.peek() === "+") {
value += this.advance();
}
if (this.match("0x") || this.match("0X")) {
value += "x";
while (!this.isEOF() && /[0-9a-fA-F]/.test(this.peek())) {
value += this.advance();
}
return "0" + value;
}
if (this.match("0b") || this.match("0B")) {
value += "b";
while (!this.isEOF() && /[01]/.test(this.peek())) {
value += this.advance();
}
return "0" + value;
}
while (!this.isEOF() && /[0-9]/.test(this.peek())) {
value += this.advance();
}
if (this.peek() === "." && /[0-9]/.test(this.peek(1))) {
value += this.advance();
while (!this.isEOF() && /[0-9]/.test(this.peek())) {
value += this.advance();
}
}
if (this.peek() === "e" || this.peek() === "E") {
value += this.advance();
if (this.peek() === "+" || this.peek() === "-") {
value += this.advance();
}
while (!this.isEOF() && /[0-9]/.test(this.peek())) {
value += this.advance();
}
}
return value;
}
createToken(type, value) {
return {
type,
value,
position: this.pos - value.length,
line: this.line,
column: this.column - value.length
};
}
nextToken() {
this.skipWhitespace();
if (this.isEOF()) {
return this.createToken("EOF", "");
}
const start = this.pos;
const char = this.peek();
if (this.match("->")) {
return this.createToken("->", "->");
}
if (this.match("..")) {
return this.createToken("..", "..");
}
if (this.match("+\u221E") || this.match("+oo")) {
return this.createToken(
"PLUS_INFINITY",
this.input.slice(start, this.pos)
);
}
if (this.match("-\u221E") || this.match("-oo")) {
return this.createToken(
"MINUS_INFINITY",
this.input.slice(start, this.pos)
);
}
if (this.match("+infinity")) {
return this.createToken("PLUS_INFINITY", "+infinity");
}
if (this.match("-infinity")) {
return this.createToken("MINUS_INFINITY", "-infinity");
}
if (/[a-zA-Z_]/.test(char)) {
const value = this.readIdentifier();
switch (value) {
case "true":
return this.createToken("TRUE", value);
case "false":
return this.createToken("FALSE", value);
case "nan":
return this.createToken("NAN", value);
case "infinity":
return this.createToken("INFINITY", value);
case "oo":
return this.createToken("INFINITY", value);
default:
return this.createToken("IDENTIFIER", value);
}
}
switch (char) {
case "|":
this.advance();
return this.createToken("|", "|");
case "&":
this.advance();
return this.createToken("&", "&");
case "!":
this.advance();
return this.createToken("!", "!");
case "^":
this.advance();
return this.createToken("^", "^");
case "(":
this.advance();
return this.createToken("(", "(");
case ")":
this.advance();
return this.createToken(")", ")");
case "<":
this.advance();
return this.createToken("<", "<");
case ">":
this.advance();
return this.createToken(">", ">");
case "[":
this.advance();
return this.createToken("[", "[");
case "]":
this.advance();
return this.createToken("]", "]");
case ",":
this.advance();
return this.createToken(",", ",");
case ":":
this.advance();
return this.createToken(":", ":");
case "?":
this.advance();
return this.createToken("?", "?");
case "*":
this.advance();
return this.createToken("*", "*");
case "+":
if (/[0-9]/.test(this.peek(1))) {
return this.createToken("NUMBER_LITERAL", this.readNumber());
}
this.advance();
return this.createToken("+", "+");
case "x":
if (/[0-9]/.test(this.peek(1))) {
this.advance();
return this.createToken("x", "x");
}
this.advance();
return this.createToken("x", "x");
}
if (char === '"' || char === "'") {
return this.createToken("STRING_LITERAL", this.readStringLiteral());
}
if (char === "`") {
return this.createToken("VERBATIM_STRING", this.readVerbatimString());
}
if (/[0-9]/.test(char) || char === "-" && /[0-9]/.test(this.peek(1))) {
const number = this.readNumber();
if (this.peek() === "x" && /[0-9]/.test(this.peek(1))) {
}
return this.createToken("NUMBER_LITERAL", number);
}
if (char === "\u221E") {
this.advance();
return this.createToken("INFINITY", "\u221E");
}
this.error(`Unexpected character: ${char}`);
}
tokenize() {
const tokens = [];
while (!this.isEOF()) {
const token = this.nextToken();
if (token) {
tokens.push(token);
if (token.type === "EOF") break;
}
}
return tokens;
}
peekToken() {
if (this.tokens.length === 0) {
const token = this.nextToken();
if (token) this.tokens.push(token);
}
return this.tokens[0] || this.createToken("EOF", "");
}
consumeToken() {
if (this.tokens.length === 0) {
const token = this.nextToken();
if (token) return token;
}
return this.tokens.shift() || this.createToken("EOF", "");
}
matchToken(type) {
const token = this.peekToken();
if (token.type === type) {
this.consumeToken();
return true;
}
return false;
}
expectToken(type) {
const token = this.consumeToken();
if (token.type !== type) {
this.error(`Expected ${type}, got ${token.type}`);
}
return token;
}
};
// src/common/type/parser.ts
var Parser = class {
lexer;