UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

400 lines (399 loc) 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isNumericLike = isNumericLike; exports.toPythonNumber = toPythonNumber; exports.toPythonInteger = toPythonInteger; exports.fromPythonInteger = fromPythonInteger; exports.pythonAbs = pythonAbs; exports.pythonPos = pythonPos; exports.pythonNeg = pythonNeg; exports.pythonTruth = pythonTruth; exports.pythonNot = pythonNot; exports.pythonAdd = pythonAdd; exports.pythonSub = pythonSub; exports.pythonMul = pythonMul; exports.pythonTrueDiv = pythonTrueDiv; exports.pythonFloorDiv = pythonFloorDiv; exports.pythonMod = pythonMod; exports.pythonPow = pythonPow; exports.pythonBitAnd = pythonBitAnd; exports.pythonBitOr = pythonBitOr; exports.pythonBitXor = pythonBitXor; exports.pythonLShift = pythonLShift; exports.pythonRShift = pythonRShift; exports.pythonInvert = pythonInvert; exports.pythonIndex = pythonIndex; exports.pythonIdentity = pythonIdentity; exports.pythonEqual = pythonEqual; exports.pythonCompare = pythonCompare; exports.pythonConcat = pythonConcat; exports.pythonContains = pythonContains; exports.pythonCountOf = pythonCountOf; exports.pythonIndexOf = pythonIndexOf; exports.pythonGetItem = pythonGetItem; function isNumericLike(value) { return typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint'; } function toPythonNumber(value, functionName) { if (typeof value === 'number') { return value; } if (typeof value === 'boolean') { return value ? 1 : 0; } if (typeof value === 'bigint') { const numericValue = Number(value); if (!Number.isSafeInteger(numericValue)) { throw new RangeError(`${functionName}() bigint values must fit within JS safe integer precision`); } return numericValue; } throw new TypeError(`${functionName}() expected a numeric value`); } function toPythonInteger(value, functionName) { if (typeof value === 'boolean') { return value ? 1n : 0n; } if (typeof value === 'bigint') { return value; } if (typeof value === 'number' && Number.isFinite(value) && Number.isSafeInteger(value)) { return BigInt(value); } throw new TypeError(`${functionName}() expected an integer value`); } function fromPythonInteger(value, functionName) { const numericValue = Number(value); if (!Number.isSafeInteger(numericValue)) { throw new RangeError(`${functionName}() result does not fit within JS safe integer precision`); } return numericValue; } function pythonAbs(value, functionName) { return Math.abs(toPythonNumber(value, functionName)); } function pythonPos(value, functionName) { return toPythonNumber(value, functionName); } function pythonNeg(value, functionName) { return -toPythonNumber(value, functionName); } function pythonTruth(value) { if (value === null || value === undefined) { return false; } if (typeof value === 'boolean') { return value; } if (typeof value === 'number') { return value !== 0; } if (typeof value === 'bigint') { return value !== 0n; } if (typeof value === 'string') { return value.length > 0; } if (Array.isArray(value)) { return value.length > 0; } if (value instanceof Set || value instanceof Map) { return value.size > 0; } if (isPythonMapping(value)) { return Object.keys(value).length > 0; } return true; } function pythonNot(value) { return !pythonTruth(value); } function pythonAdd(left, right, functionName) { if (typeof left === 'string' && typeof right === 'string') { return left + right; } if (Array.isArray(left) && Array.isArray(right)) { return [...left, ...right]; } return toPythonNumber(left, functionName) + toPythonNumber(right, functionName); } function pythonSub(left, right, functionName) { return toPythonNumber(left, functionName) - toPythonNumber(right, functionName); } function pythonMul(left, right, functionName) { if (typeof left === 'string') { return repeatString(left, right, functionName); } if (typeof right === 'string') { return repeatString(right, left, functionName); } if (Array.isArray(left)) { return repeatArray(left, right, functionName); } if (Array.isArray(right)) { return repeatArray(right, left, functionName); } return toPythonNumber(left, functionName) * toPythonNumber(right, functionName); } function pythonTrueDiv(left, right, functionName) { const divisor = toPythonNumber(right, functionName); if (divisor === 0) { throw new Error('division by zero'); } return toPythonNumber(left, functionName) / divisor; } function pythonFloorDiv(left, right, functionName) { const divisor = toPythonNumber(right, functionName); if (divisor === 0) { throw new Error('integer division or modulo by zero'); } return Math.floor(toPythonNumber(left, functionName) / divisor); } function pythonMod(left, right, functionName) { const dividend = toPythonNumber(left, functionName); const divisor = toPythonNumber(right, functionName); if (divisor === 0) { throw new Error('integer division or modulo by zero'); } return dividend - Math.floor(dividend / divisor) * divisor; } function pythonPow(left, right, functionName) { const base = toPythonNumber(left, functionName); const exponent = toPythonNumber(right, functionName); if (base === 0 && exponent < 0) { throw new Error('0.0 cannot be raised to a negative power'); } return base ** exponent; } function pythonBitAnd(left, right, functionName) { const leftInt = toPythonInteger(left, functionName); const rightInt = toPythonInteger(right, functionName); const result = leftInt & rightInt; return typeof left === 'boolean' && typeof right === 'boolean' ? result !== 0n : fromPythonInteger(result, functionName); } function pythonBitOr(left, right, functionName) { const leftInt = toPythonInteger(left, functionName); const rightInt = toPythonInteger(right, functionName); const result = leftInt | rightInt; return typeof left === 'boolean' && typeof right === 'boolean' ? result !== 0n : fromPythonInteger(result, functionName); } function pythonBitXor(left, right, functionName) { const leftInt = toPythonInteger(left, functionName); const rightInt = toPythonInteger(right, functionName); const result = leftInt ^ rightInt; return typeof left === 'boolean' && typeof right === 'boolean' ? result !== 0n : fromPythonInteger(result, functionName); } function pythonLShift(left, right, functionName) { const shift = toPythonInteger(right, functionName); if (shift < 0n) { throw new Error('negative shift count'); } return fromPythonInteger(toPythonInteger(left, functionName) << shift, functionName); } function pythonRShift(left, right, functionName) { const shift = toPythonInteger(right, functionName); if (shift < 0n) { throw new Error('negative shift count'); } return fromPythonInteger(toPythonInteger(left, functionName) >> shift, functionName); } function pythonInvert(value, functionName) { return fromPythonInteger(~toPythonInteger(value, functionName), functionName); } function pythonIndex(value, functionName) { return fromPythonInteger(toPythonInteger(value, functionName), functionName); } function pythonIdentity(left, right) { return Object.is(left, right); } function pythonEqual(left, right) { if (isNumericLike(left) && isNumericLike(right)) { return numericEqual(left, right); } if (typeof left === 'string' && typeof right === 'string') { return left === right; } if (Array.isArray(left) && Array.isArray(right)) { if (left.length !== right.length) { return false; } return left.every((value, index) => pythonEqual(value, right[index])); } if (isPythonMapping(left) && isPythonMapping(right)) { const leftKeys = Object.keys(left); const rightKeys = Object.keys(right); if (leftKeys.length !== rightKeys.length) { return false; } return leftKeys.every((key) => Object.hasOwn(right, key) && pythonEqual(left[key], right[key])); } return left === right; } function pythonCompare(left, right, functionName) { if (isNumericLike(left) && isNumericLike(right)) { const leftNumber = toPythonNumber(left, functionName); const rightNumber = toPythonNumber(right, functionName); if (Number.isNaN(leftNumber) || Number.isNaN(rightNumber)) { return Number.NaN; } return leftNumber === rightNumber ? 0 : leftNumber < rightNumber ? -1 : 1; } if (typeof left === 'string' && typeof right === 'string') { return left === right ? 0 : left < right ? -1 : 1; } throw new TypeError(`${functionName}() does not support comparing these values`); } function pythonConcat(left, right) { if (typeof left === 'string' && typeof right === 'string') { return left + right; } if (Array.isArray(left) && Array.isArray(right)) { return [...left, ...right]; } throw new TypeError('concat() expects two strings or two arrays'); } function pythonContains(container, item) { if (typeof container === 'string') { if (typeof item !== 'string') { throw new TypeError('contains() string containers require a string search value'); } return container.includes(item); } if (Array.isArray(container)) { return container.some((value) => pythonEqual(value, item)); } if (isPythonMapping(container)) { return Object.hasOwn(container, String(item)); } throw new TypeError('contains() expects a string, array, or plain object container'); } function pythonCountOf(sequence, item) { if (typeof sequence === 'string') { if (typeof item !== 'string') { throw new TypeError('countOf() string sequences require a string search value'); } return countSubstring(sequence, item); } if (Array.isArray(sequence)) { return sequence.filter((value) => pythonEqual(value, item)).length; } throw new TypeError('countOf() expects a string or array sequence'); } function pythonIndexOf(sequence, item) { if (typeof sequence === 'string') { if (typeof item !== 'string') { throw new TypeError('indexOf() string sequences require a string search value'); } const index = sequence.indexOf(item); if (index === -1) { throw new Error('sequence.index(x): x not in sequence'); } return index; } if (Array.isArray(sequence)) { const index = sequence.findIndex((value) => pythonEqual(value, item)); if (index === -1) { throw new Error('sequence.index(x): x not in sequence'); } return index; } throw new TypeError('indexOf() expects a string or array sequence'); } function pythonGetItem(target, key) { if (typeof target === 'string' || Array.isArray(target)) { const index = normalizeIndex(target.length, pythonIndex(key, 'getitem')); if (index < 0 || index >= target.length) { throw new Error('sequence index out of range'); } return target[index]; } if (isPythonMapping(target)) { const propertyKey = String(key); if (!Object.hasOwn(target, propertyKey)) { throw new Error('key not found'); } return target[propertyKey]; } throw new TypeError('getitem() expects a string, array, or plain object target'); } function repeatString(value, count, functionName) { const normalized = normalizeRepeatCount(count, functionName); return value.repeat(normalized); } function repeatArray(value, count, functionName) { const normalized = normalizeRepeatCount(count, functionName); const out = []; for (let index = 0; index < normalized; index += 1) { out.push(...value); } return out; } function normalizeRepeatCount(count, functionName) { const bigintCount = toPythonInteger(count, functionName); if (bigintCount <= 0n) { return 0; } return fromPythonInteger(bigintCount, functionName); } function normalizeIndex(length, index) { return index < 0 ? length + index : index; } function countSubstring(haystack, needle) { if (needle === '') { return haystack.length + 1; } let count = 0; let offset = 0; while (offset <= haystack.length) { const next = haystack.indexOf(needle, offset); if (next === -1) { break; } count += 1; offset = next + needle.length; } return count; } function numericEqual(left, right) { if (typeof left === 'bigint' || typeof right === 'bigint') { return bigintEqual(left, right); } const leftNumber = toComparableNumber(left); const rightNumber = toComparableNumber(right); return !Number.isNaN(leftNumber) && !Number.isNaN(rightNumber) && leftNumber === rightNumber; } function toComparableNumber(value) { return typeof value === 'boolean' ? (value ? 1 : 0) : typeof value === 'bigint' ? Number(value) : value; } function bigintEqual(left, right) { const leftBigint = toComparableBigint(left); const rightBigint = toComparableBigint(right); if (leftBigint === null || rightBigint === null) { return false; } return leftBigint === rightBigint; } function toComparableBigint(value) { if (typeof value === 'bigint') { return value; } if (typeof value === 'boolean') { return value ? 1n : 0n; } if (Number.isFinite(value) && Number.isSafeInteger(value)) { return BigInt(value); } return null; } function isPythonMapping(value) { if (typeof value !== 'object' || value === null || Array.isArray(value)) { return false; } return Object.prototype.toString.call(value) === '[object Object]'; }