@extra2001/compute-engine
Version:
Symbolic computing and numeric evaluations for JavaScript and Node.js
389 lines (385 loc) • 10.5 kB
JavaScript
/** Compute Engine 0.28.0 */
// src/common/json5.ts
var JSON5 = class {
static parse(input) {
const parser = new JSON5Parser(input);
const value = parser.parseValue();
parser.skipWhitespace();
if (!parser.isAtEnd()) {
throw parser.error(
`Unexpected token '${parser.currentChar()}' after parsing complete value`
);
}
return value;
}
};
var JSON5Parser = class {
constructor(input) {
this.index = 0;
this.text = input;
}
parseValue() {
this.skipWhitespace();
if (this.isAtEnd()) {
throw this.error("Unexpected end of input");
}
const ch = this.currentChar();
if (ch === "{") return this.parseObject();
if (ch === "[") return this.parseArray();
if (ch === '"' || ch === "'") return this.parseString();
if (ch === "-" || ch === "+" || ch >= "0" && ch <= "9" || ch === ".")
return this.parseNumber();
return this.parseIdentifier();
}
parseObject() {
const obj = {};
this.expectChar("{");
this.skipWhitespace();
if (this.currentChar() === "}") {
this.index++;
return obj;
}
while (true) {
this.skipWhitespace();
let key;
const ch = this.currentChar();
if (ch === '"' || ch === "'") {
key = this.parseString();
} else {
key = this.parseIdentifier();
}
this.skipWhitespace();
this.expectChar(":");
this.skipWhitespace();
const value = this.parseValue();
obj[key] = value;
this.skipWhitespace();
if (this.currentChar() === ",") {
this.index++;
this.skipWhitespace();
if (this.currentChar() === "}") {
this.index++;
break;
}
} else if (this.currentChar() === "}") {
this.index++;
break;
} else {
throw this.error(
`Expected ',' or '}' in object but found '${this.currentChar()}'`
);
}
}
return obj;
}
parseArray() {
const arr = [];
this.expectChar("[");
this.skipWhitespace();
if (this.currentChar() === "]") {
this.index++;
return arr;
}
while (true) {
this.skipWhitespace();
arr.push(this.parseValue());
this.skipWhitespace();
if (this.currentChar() === ",") {
this.index++;
this.skipWhitespace();
if (this.currentChar() === "]") {
this.index++;
break;
}
} else if (this.currentChar() === "]") {
this.index++;
break;
} else {
throw this.error(
`Expected ',' or ']' in array but found '${this.currentChar()}'`
);
}
}
return arr;
}
parseString() {
const quote = this.currentChar();
if (quote !== '"' && quote !== "'") {
throw this.error(`String should start with a quote, got '${quote}'`);
}
this.index++;
let result = "";
while (!this.isAtEnd()) {
const ch = this.currentChar();
if (ch === quote) {
this.index++;
return result;
}
if (ch === "\\") {
this.index++;
if (this.isAtEnd()) {
throw this.error("Unterminated escape sequence in string");
}
const esc = this.currentChar();
switch (esc) {
case "b":
result += "\b";
break;
case "f":
result += "\f";
break;
case "n":
result += "\n";
break;
case "r":
result += "\r";
break;
case "t":
result += " ";
break;
case "v":
result += "\v";
break;
case "\\":
result += "\\";
break;
case "'":
result += "'";
break;
case '"':
result += '"';
break;
case "0":
result += "\0";
break;
case "u": {
this.index++;
const hex = this.text.substr(this.index, 4);
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
throw this.error(`Invalid Unicode escape sequence: \\u${hex}`);
}
result += String.fromCharCode(parseInt(hex, 16));
this.index += 3;
break;
}
default:
result += esc;
}
this.index++;
} else {
result += ch;
this.index++;
}
}
throw this.error("Unterminated string literal");
}
parseNumber() {
const start = this.index;
if (this.text.startsWith("-Infinity", this.index)) {
this.index += "-Infinity".length;
return -Infinity;
}
if (this.text.startsWith("+Infinity", this.index)) {
this.index += "+Infinity".length;
return Infinity;
}
if (this.text.startsWith("Infinity", this.index)) {
this.index += "Infinity".length;
return Infinity;
}
while (!this.isAtEnd() && /[0-9+\-_.eE]/.test(this.currentChar())) {
this.index++;
}
const token = this.text.slice(start, this.index);
const normalized = token.replace(/_/g, "");
const num = Number(normalized);
if (isNaN(num)) {
throw this.error(`Invalid number: ${token}`);
}
return num;
}
parseIdentifier() {
const start = this.index;
const firstChar = this.currentChar();
if (!/[a-zA-Z$_]/.test(firstChar)) {
throw this.error(`Unexpected token '${firstChar}'`);
}
this.index++;
while (!this.isAtEnd()) {
const ch = this.currentChar();
if (!/[a-zA-Z0-9$_]/.test(ch)) break;
this.index++;
}
const token = this.text.slice(start, this.index);
if (token === "true") return true;
if (token === "false") return false;
if (token === "null") return null;
if (token === "Infinity") return Infinity;
if (token === "NaN") return NaN;
return token;
}
skipWhitespace() {
while (!this.isAtEnd()) {
const ch = this.currentChar();
if (/\s/.test(ch)) {
this.index++;
continue;
}
if (ch === "/") {
const next = this.peekChar(1);
if (next === "/") {
this.index += 2;
while (!this.isAtEnd() && this.currentChar() !== "\n") {
this.index++;
}
continue;
} else if (next === "*") {
this.index += 2;
while (!this.isAtEnd() && !(this.currentChar() === "*" && this.peekChar(1) === "/")) {
this.index++;
}
if (this.isAtEnd()) {
throw this.error("Unterminated multi-line comment");
}
this.index += 2;
continue;
}
}
break;
}
}
expectChar(expected) {
if (this.currentChar() !== expected) {
throw this.error(
`Expected '${expected}' but found '${this.currentChar()}'`
);
}
this.index++;
}
currentChar() {
return this.text[this.index];
}
peekChar(offset) {
return this.text[this.index + offset];
}
isAtEnd() {
return this.index >= this.text.length;
}
error(message) {
return new Error(`${message} at position ${this.index}`);
}
};
// src/math-json/utils.ts
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 isFunctionObject(expr) {
return expr !== null && typeof expr === "object" && "fn" in expr;
}
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) return null;
if (expr.at(0) !== "'" || expr.at(-1) !== "'") return null;
return expr.substring(1, expr.length - 1);
}
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 symbol(expr) {
if (typeof expr === "string") {
if (/^[+-]?[0-9\.]/.test(expr)) return null;
if (expr.length >= 2 && expr[0] === "'" && expr[expr.length - 1] === "'")
return null;
return expr;
}
if (expr === null || expr === void 0) return null;
const s = isSymbolObject(expr) ? expr.sym : expr;
if (typeof s !== "string") return null;
return s;
}
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 (typeof expr === "object" && !("sym" in expr) && !("num" in expr) && !("str" in expr) && !("fn" in expr)) {
return expr;
}
if (typeof expr === "string" && expr[0] === "{" && expr[expr.length - 1] === "}") {
try {
return JSON5.parse(expr);
} catch {
return null;
}
}
const kv = keyValuePair(expr);
if (kv) return { [kv[0]]: kv[1] };
if (operator(expr) === "Dictionary") {
const result = {};
const ops = operands(expr);
for (let i = 1; i < nops(expr); i++) {
const kv2 = keyValuePair(ops[i]);
if (kv2) result[kv2[0]] = kv2[1];
}
return result;
}
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;
}
// src/math-json.ts
var version = "0.28.0";
export {
dictionaryFromExpression,
isFunctionObject,
isStringObject,
isSymbolObject,
mapArgs,
operand,
operator,
stringValue,
symbol,
version
};